PEEL Shopping
Open source ecommerce : PEEL Shopping
tcpdf.php
Go to the documentation of this file.
1 <?php
2 //============================================================+
3 // File name : tcpdf.php
4 // Version : 5.9.202
5 // Begin : 2002-08-03
6 // Last Update : 2012-12-16
7 // Author : Nicola Asuni - Tecnick.com LTD - Manor Coach House, Church Hill, Aldershot, Hants, GU12 4RQ, UK - www.tecnick.com - info@tecnick.com
8 // License : http://www.tecnick.com/pagefiles/tcpdf/LICENSE.TXT GNU-LGPLv3
9 // -------------------------------------------------------------------
10 // Copyright (C) 2002-2012 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 Extention, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS, Datamatrix, QR-Code, PDF417;
50 // * JPEG and PNG ICC profiles, Grayscale, RGB, CMYK, Spot Colors and Transparencies;
51 // * automatic page header and footer management;
52 // * document encryption up to 256 bit and digital signature certifications;
53 // * transactions to UNDO commands;
54 // * PDF annotations, including links, text and file attachments;
55 // * text rendering modes (fill, stroke and clipping);
56 // * multiple columns mode;
57 // * no-write page regions;
58 // * bookmarks, named destinations and table of content;
59 // * text hyphenation;
60 // * text stretching and spacing (tracking);
61 // * automatic page break, line break and text alignments including justification;
62 // * automatic page numbering and page groups;
63 // * move and delete pages;
64 // * page compression (requires php-zlib extension);
65 // * XOBject Templates;
66 // * Layers and object visibility.
67 // * PDF/A-1b support.
68 //
69 // -----------------------------------------------------------
70 // THANKS TO:
71 //
72 // Olivier Plathey (http://www.fpdf.org) for original FPDF.
73 // Efthimios Mavrogeorgiadis (emavro@yahoo.com) for suggestions on RTL language support.
74 // Klemen Vodopivec (http://www.fpdf.de/downloads/addons/37/) for Encryption algorithm.
75 // Warren Sherliker (wsherliker@gmail.com) for better image handling.
76 // dullus for text Justification.
77 // Bob Vincent (pillarsdotnet@users.sourceforge.net) for <li> value attribute.
78 // Patrick Benny for text stretch suggestion on Cell().
79 // Johannes Güntert for JavaScript support.
80 // Denis Van Nuffelen for Dynamic Form.
81 // Jacek Czekaj for multibyte justification
82 // Anthony Ferrara for the reintroduction of legacy image methods.
83 // Sourceforge user 1707880 (hucste) for line-trough mode.
84 // Larry Stanbery for page groups.
85 // Martin Hall-May for transparency.
86 // Aaron C. Spike for Polycurve method.
87 // Mohamad Ali Golkar, Saleh AlMatrafe, Charles Abbott for Arabic and Persian support.
88 // Moritz Wagner and Andreas Wurmser for graphic functions.
89 // Andrew Whitehead for core fonts support.
90 // Esteban Joël Marín for OpenType font conversion.
91 // Teus Hagen for several suggestions and fixes.
92 // Yukihiro Nakadaira for CID-0 CJK fonts fixes.
93 // Kosmas Papachristos for some CSS improvements.
94 // Marcel Partap for some fixes.
95 // Won Kyu Park for several suggestions, fixes and patches.
96 // Dominik Dzienia for QR-code support.
97 // Laurent Minguet for some suggestions.
98 // Christian Deligant for some suggestions and fixes.
99 // Travis Harris for crop mark suggestion.
100 // Aleksey Kuznetsov for some suggestions and text shadows.
101 // Jim Hanlon for several suggestions and patches.
102 // Anyone else that has reported a bug or sent a suggestion.
103 //============================================================+
104 
145 // Main configuration file. Define the K_TCPDF_EXTERNAL_CONFIG constant to skip this file.
146 require_once(dirname(__FILE__).'/config/tcpdf_config.php');
147 
157 class TCPDF {
158 
159  // private properties
160 
165  private $tcpdf_version = '5.9.202';
166 
167  // Protected properties
168 
173  protected $page;
174 
179  protected $n;
180 
185  protected $offsets = array();
186 
191  protected $pageobjects = array();
192 
197  protected $buffer;
198 
203  protected $pages = array();
204 
209  protected $state;
210 
215  protected $compress;
216 
221  protected $CurOrientation;
222 
227  protected $pagedim = array();
228 
233  protected $k;
234 
239  protected $fwPt;
240 
245  protected $fhPt;
246 
251  protected $wPt;
252 
257  protected $hPt;
258 
263  protected $w;
264 
269  protected $h;
270 
275  protected $lMargin;
276 
281  protected $rMargin;
282 
287  protected $clMargin;
288 
293  protected $crMargin;
294 
299  protected $tMargin;
300 
305  protected $bMargin;
306 
312  protected $cell_padding = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0);
313 
319  protected $cell_margin = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0);
320 
325  protected $x;
326 
331  protected $y;
332 
337  protected $lasth;
338 
343  protected $LineWidth;
344 
349  protected $CoreFonts;
350 
355  protected $fonts = array();
356 
361  protected $FontFiles = array();
362 
367  protected $diffs = array();
368 
373  protected $images = array();
374 
379  protected $cached_files = array();
380 
385  protected $PageAnnots = array();
386 
391  protected $links = array();
392 
397  protected $FontFamily;
398 
403  protected $FontStyle;
404 
410  protected $FontAscent;
411 
417  protected $FontDescent;
418 
423  protected $underline;
424 
429  protected $overline;
430 
435  protected $CurrentFont;
436 
441  protected $FontSizePt;
442 
447  protected $FontSize;
448 
453  protected $DrawColor;
454 
459  protected $FillColor;
460 
465  protected $TextColor;
466 
471  protected $ColorFlag;
472 
477  protected $AutoPageBreak;
478 
483  protected $PageBreakTrigger;
484 
489  protected $InHeader = false;
490 
495  protected $InFooter = false;
496 
501  protected $ZoomMode;
502 
507  protected $LayoutMode;
508 
513  protected $docinfounicode = true;
514 
519  protected $title = '';
520 
525  protected $subject = '';
526 
531  protected $author = '';
532 
537  protected $keywords = '';
538 
543  protected $creator = '';
544 
549  protected $starting_page_number = 1;
550 
555  protected $alias_tot_pages = '{:ptp:}';
556 
561  protected $alias_num_page = '{:pnp:}';
562 
567  protected $alias_group_tot_pages = '{:ptg:}';
568 
573  protected $alias_group_num_page = '{:png:}';
574 
579  protected $alias_right_shift = '{rsc:';
580 
587  protected $img_rb_x;
588 
595  protected $img_rb_y;
596 
603  protected $imgscale = 1;
604 
611  protected $isunicode = false;
612 
619  protected $unicode;
620 
627  protected $encmaps;
628 
634  protected $PDFVersion = '1.7';
635 
640  protected $header_xobjid = -1;
641 
646  protected $header_xobj_autoreset = false;
647 
652  protected $header_margin;
653 
658  protected $footer_margin;
659 
665  protected $original_lMargin;
666 
672  protected $original_rMargin;
673 
678  protected $header_font;
679 
684  protected $footer_font;
685 
690  protected $l;
691 
696  protected $barcode = false;
697 
702  protected $print_header = true;
703 
708  protected $print_footer = true;
709 
714  protected $header_logo = '';
715 
720  protected $header_logo_width = 30;
721 
726  protected $header_title = '';
727 
732  protected $header_string = '';
733 
739  protected $header_text_color = array(0,0,0);
740 
746  protected $header_line_color = array(0,0,0);
747 
753  protected $footer_text_color = array(0,0,0);
754 
760  protected $footer_line_color = array(0,0,0);
761 
767  protected $txtshadow = array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=>'Normal');
768 
773  protected $default_table_columns = 4;
774 
775  // variables for html parser
776 
781  protected $HREF = array();
782 
787  protected $fontlist = array();
788 
793  protected $fgcolor;
794 
799  protected $listordered = array();
800 
805  protected $listcount = array();
806 
811  protected $listnum = 0;
812 
817  protected $listindent = 0;
818 
823  protected $listindentlevel = 0;
824 
829  protected $bgcolor;
830 
835  protected $tempfontsize = 10;
836 
841  protected $lispacer = '';
842 
848  protected $encoding = 'UTF-8';
849 
856 
862  protected $rtl = false;
863 
869  protected $tmprtl = false;
870 
871  // --- Variables used for document encryption:
872 
878  protected $encrypted;
879 
885  protected $encryptdata = array();
886 
892  protected $last_enc_key;
893 
899  protected $last_enc_key_c;
900 
905  protected $enc_padding = "\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A";
906 
912  protected $file_id;
913 
914  // --- bookmark ---
915 
921  protected $outlines = array();
922 
928  protected $OutlineRoot;
929 
930  // --- javascript and form ---
931 
937  protected $javascript = '';
938 
944  protected $n_js;
945 
951  protected $linethrough;
952 
958  protected $ur = array();
959 
965  protected $dpi = 72;
966 
972  protected $newpagegroup = array();
973 
979  protected $pagegroups = array();
980 
986  protected $currpagegroup = 0;
987 
993  protected $extgstates;
994 
1000  protected $jpeg_quality;
1001 
1008 
1015 
1021  protected $PageMode;
1022 
1028  protected $gradients = array();
1029 
1035  protected $intmrk = array();
1036 
1042  protected $bordermrk = array();
1043 
1049  protected $emptypagemrk = array();
1050 
1056  protected $cntmrk = array();
1057 
1063  protected $footerpos = array();
1064 
1070  protected $footerlen = array();
1071 
1077  protected $newline = true;
1078 
1084  protected $endlinex = 0;
1085 
1091  protected $linestyleWidth = '';
1092 
1098  protected $linestyleCap = '0 J';
1099 
1105  protected $linestyleJoin = '0 j';
1106 
1112  protected $linestyleDash = '[] 0 d';
1113 
1119  protected $openMarkedContent = false;
1120 
1126  protected $htmlvspace = 0;
1127 
1133  protected $spot_colors = array();
1134 
1140  protected $lisymbol = '';
1141 
1147  protected $epsmarker = 'x#!#EPS#!#x';
1148 
1154  protected $transfmatrix = array();
1155 
1161  protected $transfmatrix_key = 0;
1162 
1168  protected $booklet = false;
1169 
1175  protected $feps = 0.005;
1176 
1182  protected $tagvspaces = array();
1183 
1189  protected $customlistindent = -1;
1190 
1196  protected $opencell = true;
1197 
1203  protected $embeddedfiles = array();
1204 
1210  protected $premode = false;
1211 
1218  protected $transfmrk = array();
1219 
1225  protected $htmlLinkColorArray = array(0, 0, 255);
1226 
1232  protected $htmlLinkFontStyle = 'U';
1233 
1239  protected $numpages = 0;
1240 
1246  protected $pagelen = array();
1247 
1253  protected $numimages = 0;
1254 
1260  protected $imagekeys = array();
1261 
1267  protected $bufferlen = 0;
1268 
1274  protected $diskcache = false;
1275 
1281  protected $numfonts = 0;
1282 
1288  protected $fontkeys = array();
1289 
1295  protected $font_obj_ids = array();
1296 
1302  protected $pageopen = array();
1303 
1309  protected $default_monospaced_font = 'courier';
1310 
1316  protected $objcopy;
1317 
1323  protected $cache_file_length = array();
1324 
1330  protected $thead = '';
1331 
1337  protected $theadMargins = array();
1338 
1344  protected $cache_UTF8StringToArray = array();
1345 
1352 
1359 
1365  protected $sign = false;
1366 
1372  protected $signature_data = array();
1373 
1379  protected $signature_max_length = 11742;
1380 
1386  protected $signature_appearance = array('page' => 1, 'rect' => '0 0 0 0');
1387 
1393  protected $empty_signature_appearance = array();
1394 
1400  protected $re_spaces = '/[^\S\xa0]/';
1401 
1407  protected $re_space = array('p' => '[^\S\xa0]', 'm' => '');
1408 
1414  protected $sig_obj_id = 0;
1415 
1421  protected $byterange_string = '/ByteRange[0 ********** ********** **********]';
1422 
1428  protected $sig_annot_ref = '***SIGANNREF*** 0 R';
1429 
1435  protected $page_obj_id = array();
1436 
1442  protected $form_obj_id = array();
1443 
1449  protected $default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
1450 
1456  protected $js_objects = array();
1457 
1463  protected $form_action = '';
1464 
1470  protected $form_enctype = 'application/x-www-form-urlencoded';
1471 
1477  protected $form_mode = 'post';
1478 
1484  protected $annotation_fonts = array();
1485 
1491  protected $radiobutton_groups = array();
1492 
1498  protected $radio_groups = array();
1499 
1505  protected $textindent = 0;
1506 
1513 
1519  protected $start_transaction_y = 0;
1520 
1526  protected $inthead = false;
1527 
1533  protected $columns = array();
1534 
1540  protected $num_columns = 1;
1541 
1547  protected $current_column = 0;
1548 
1554  protected $column_start_page = 0;
1555 
1561  protected $maxselcol = array('page' => 0, 'column' => 0);
1562 
1568  protected $colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
1569 
1575  protected $textrendermode = 0;
1576 
1582  protected $textstrokewidth = 0;
1583 
1589  protected $strokecolor;
1590 
1596  protected $pdfunit = 'mm';
1597 
1602  protected $tocpage = false;
1603 
1609  protected $rasterize_vector_images = false;
1610 
1616  protected $font_subsetting = true;
1617 
1623  protected $default_graphic_vars = array();
1624 
1630  protected $xobjects = array();
1631 
1637  protected $inxobj = false;
1638 
1644  protected $xobjid = '';
1645 
1651  protected $font_stretching = 100;
1652 
1658  protected $font_spacing = 0;
1659 
1666  protected $page_regions = array();
1667 
1672  protected $check_page_regions = true;
1673 
1679  protected $webcolor = array();
1680 
1686  protected $spotcolor = array();
1687 
1693  protected $pdflayers = array();
1694 
1700  protected $dests = array();
1701 
1707  protected $n_dests;
1708 
1714  protected $svgdir = '';
1715 
1721  protected $svgunit = 'px';
1722 
1728  protected $svggradients = array();
1729 
1735  protected $svggradientid = 0;
1736 
1742  protected $svgdefsmode = false;
1743 
1749  protected $svgdefs = array();
1750 
1756  protected $svgclipmode = false;
1757 
1763  protected $svgclippaths = array();
1764 
1770  protected $svgcliptm = array();
1771 
1777  protected $svgclipid = 0;
1778 
1784  protected $svgtext = '';
1785 
1791  protected $svgtextmode = array();
1792 
1798  protected $svginheritprop = array('clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cursor', 'direction', 'fill', 'fill-opacity', 'fill-rule', 'font', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'glyph-orientation-horizontal', 'glyph-orientation-vertical', 'image-rendering', 'kerning', 'letter-spacing', 'marker', 'marker-end', 'marker-mid', 'marker-start', 'pointer-events', 'shape-rendering', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'text-anchor', 'text-rendering', 'visibility', 'word-spacing', 'writing-mode');
1799 
1805  protected $svgstyles = array(array(
1806  'alignment-baseline' => 'auto',
1807  'baseline-shift' => 'baseline',
1808  'clip' => 'auto',
1809  'clip-path' => 'none',
1810  'clip-rule' => 'nonzero',
1811  'color' => 'black',
1812  'color-interpolation' => 'sRGB',
1813  'color-interpolation-filters' => 'linearRGB',
1814  'color-profile' => 'auto',
1815  'color-rendering' => 'auto',
1816  'cursor' => 'auto',
1817  'direction' => 'ltr',
1818  'display' => 'inline',
1819  'dominant-baseline' => 'auto',
1820  'enable-background' => 'accumulate',
1821  'fill' => 'black',
1822  'fill-opacity' => 1,
1823  'fill-rule' => 'nonzero',
1824  'filter' => 'none',
1825  'flood-color' => 'black',
1826  'flood-opacity' => 1,
1827  'font' => '',
1828  'font-family' => 'helvetica',
1829  'font-size' => 'medium',
1830  'font-size-adjust' => 'none',
1831  'font-stretch' => 'normal',
1832  'font-style' => 'normal',
1833  'font-variant' => 'normal',
1834  'font-weight' => 'normal',
1835  'glyph-orientation-horizontal' => '0deg',
1836  'glyph-orientation-vertical' => 'auto',
1837  'image-rendering' => 'auto',
1838  'kerning' => 'auto',
1839  'letter-spacing' => 'normal',
1840  'lighting-color' => 'white',
1841  'marker' => '',
1842  'marker-end' => 'none',
1843  'marker-mid' => 'none',
1844  'marker-start' => 'none',
1845  'mask' => 'none',
1846  'opacity' => 1,
1847  'overflow' => 'auto',
1848  'pointer-events' => 'visiblePainted',
1849  'shape-rendering' => 'auto',
1850  'stop-color' => 'black',
1851  'stop-opacity' => 1,
1852  'stroke' => 'none',
1853  'stroke-dasharray' => 'none',
1854  'stroke-dashoffset' => 0,
1855  'stroke-linecap' => 'butt',
1856  'stroke-linejoin' => 'miter',
1857  'stroke-miterlimit' => 4,
1858  'stroke-opacity' => 1,
1859  'stroke-width' => 1,
1860  'text-anchor' => 'start',
1861  'text-decoration' => 'none',
1862  'text-rendering' => 'auto',
1863  'unicode-bidi' => 'normal',
1864  'visibility' => 'visible',
1865  'word-spacing' => 'normal',
1866  'writing-mode' => 'lr-tb',
1867  'text-color' => 'black',
1868  'transfmatrix' => array(1, 0, 0, 1, 0, 0)
1869  ));
1870 
1876  protected $force_srgb = false;
1877 
1883  protected $pdfa_mode = false;
1884 
1891 
1898 
1904  protected $custom_xmp = '';
1905 
1912  protected $overprint = array('OP' => false, 'op' => false, 'OPM' => 0);
1913 
1920  protected $alpha = array('CA' => 1, 'ca' => 1, 'BM' => '/Normal', 'AIS' => false);
1921 
1927  protected $page_boxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox');
1928 
1934  protected $pdfproducer;
1935 
1941  protected $tcpdflink = true;
1942 
1948  protected $gdgammacache = array();
1949 
1950  //------------------------------------------------------------
1951  // METHODS
1952  //------------------------------------------------------------
1953 
1967  public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false, $pdfa=false) {
1968  /* Set internal character encoding to ASCII */
1969  if (function_exists('mb_internal_encoding') AND mb_internal_encoding()) {
1970  $this->internal_encoding = mb_internal_encoding();
1971  // DESACTIVATION PAR GB CAR INTERFERENCE AVEC LE RESTE DE PEEL : mb_internal_encoding('ASCII');
1972  }
1973  // get array of HTML colors
1974  require(dirname(__FILE__).'/htmlcolors.php');
1975  $this->webcolor = $webcolor;
1976  // get array of custom spot colors
1977  if (file_exists(dirname(__FILE__).'/spotcolors.php')) {
1978  require(dirname(__FILE__).'/spotcolors.php');
1979  $this->spotcolor = $spotcolor;
1980  } else {
1981  $this->spotcolor = array();
1982  }
1983  require_once(dirname(__FILE__).'/unicode_data.php');
1984  $this->unicode = new TCPDF_UNICODE_DATA();
1985  require_once(dirname(__FILE__).'/encodings_maps.php');
1986  $this->encmaps = new TCPDF_ENCODING_MAPS();
1987  $this->font_obj_ids = array();
1988  $this->page_obj_id = array();
1989  $this->form_obj_id = array();
1990  // set pdf/a mode
1991  $this->pdfa_mode = $pdfa;
1992  $this->force_srgb = false;
1993  // set disk caching
1994  $this->diskcache = $diskcache ? true : false;
1995  // set language direction
1996  $this->rtl = false;
1997  $this->tmprtl = false;
1998  // some checks
1999  $this->_dochecks();
2000  // initialization of properties
2001  $this->isunicode = $unicode;
2002  $this->page = 0;
2003  $this->transfmrk[0] = array();
2004  $this->pagedim = array();
2005  $this->n = 2;
2006  $this->buffer = '';
2007  $this->pages = array();
2008  $this->state = 0;
2009  $this->fonts = array();
2010  $this->FontFiles = array();
2011  $this->diffs = array();
2012  $this->images = array();
2013  $this->links = array();
2014  $this->gradients = array();
2015  $this->InFooter = false;
2016  $this->lasth = 0;
2017  $this->FontFamily = defined('PDF_FONT_NAME_MAIN')?PDF_FONT_NAME_MAIN:'helvetica';
2018  $this->FontStyle = '';
2019  $this->FontSizePt = 12;
2020  $this->underline = false;
2021  $this->overline = false;
2022  $this->linethrough = false;
2023  $this->DrawColor = '0 G';
2024  $this->FillColor = '0 g';
2025  $this->TextColor = '0 g';
2026  $this->ColorFlag = false;
2027  $this->pdflayers = array();
2028  // encryption values
2029  $this->encrypted = false;
2030  $this->last_enc_key = '';
2031  // standard Unicode fonts
2032  $this->CoreFonts = array(
2033  'courier'=>'Courier',
2034  'courierB'=>'Courier-Bold',
2035  'courierI'=>'Courier-Oblique',
2036  'courierBI'=>'Courier-BoldOblique',
2037  'helvetica'=>'Helvetica',
2038  'helveticaB'=>'Helvetica-Bold',
2039  'helveticaI'=>'Helvetica-Oblique',
2040  'helveticaBI'=>'Helvetica-BoldOblique',
2041  'times'=>'Times-Roman',
2042  'timesB'=>'Times-Bold',
2043  'timesI'=>'Times-Italic',
2044  'timesBI'=>'Times-BoldItalic',
2045  'symbol'=>'Symbol',
2046  'zapfdingbats'=>'ZapfDingbats'
2047  );
2048  // set scale factor
2049  $this->setPageUnit($unit);
2050  // set page format and orientation
2051  $this->setPageFormat($format, $orientation);
2052  // page margins (1 cm)
2053  $margin = 28.35 / $this->k;
2054  $this->SetMargins($margin, $margin);
2055  $this->clMargin = $this->lMargin;
2056  $this->crMargin = $this->rMargin;
2057  // internal cell padding
2058  $cpadding = $margin / 10;
2059  $this->setCellPaddings($cpadding, 0, $cpadding, 0);
2060  // cell margins
2061  $this->setCellMargins(0, 0, 0, 0);
2062  // line width (0.2 mm)
2063  $this->LineWidth = 0.57 / $this->k;
2064  $this->linestyleWidth = sprintf('%F w', ($this->LineWidth * $this->k));
2065  $this->linestyleCap = '0 J';
2066  $this->linestyleJoin = '0 j';
2067  $this->linestyleDash = '[] 0 d';
2068  // automatic page break
2069  $this->SetAutoPageBreak(true, (2 * $margin));
2070  // full width display mode
2071  $this->SetDisplayMode('fullwidth');
2072  // compression
2073  $this->SetCompression();
2074  // set default PDF version number
2075  $this->setPDFVersion();
2076  $this->pdfproducer = "\x54\x43\x50\x44\x46\x20".$this->tcpdf_version."\x20\x28\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67\x29";
2077  $this->tcpdflink = true;
2078  $this->encoding = $encoding;
2079  $this->HREF = array();
2080  $this->getFontsList();
2081  $this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0);
2082  $this->strokecolor = array('R' => 0, 'G' => 0, 'B' => 0);
2083  $this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255);
2084  $this->extgstates = array();
2085  $this->setTextShadow();
2086  // user's rights
2087  $this->sign = false;
2088  $this->ur['enabled'] = false;
2089  $this->ur['document'] = '/FullSave';
2090  $this->ur['annots'] = '/Create/Delete/Modify/Copy/Import/Export';
2091  $this->ur['form'] = '/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate';
2092  $this->ur['signature'] = '/Modify';
2093  $this->ur['ef'] = '/Create/Delete/Modify/Import';
2094  $this->ur['formex'] = '';
2095  $this->signature_appearance = array('page' => 1, 'rect' => '0 0 0 0');
2096  $this->empty_signature_appearance = array();
2097  // set default JPEG quality
2098  $this->jpeg_quality = $GLOBALS['site_parameters']['jpeg_quality'];
2099  // initialize some settings
2100  $this->utf8Bidi(array(''), '');
2101  // set default font
2102  $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
2103  // check if PCRE Unicode support is enabled
2104  if ($this->isunicode AND (@preg_match('/\pL/u', 'a') == 1)) {
2105  // PCRE unicode support is turned ON
2106  // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
2107  // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
2108  // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
2109  //$this->setSpacesRE('/[^\S\P{Z}\P{Lo}\xa0]/u');
2110  $this->setSpacesRE('/[^\S\P{Z}\xa0]/u');
2111  } else {
2112  // PCRE unicode support is turned OFF
2113  $this->setSpacesRE('/[^\S\xa0]/');
2114  }
2115  $this->default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
2116  // set file ID for trailer
2117  $serformat = (is_array($format) ? serialize($format) : $format);
2118  $this->file_id = md5($this->getRandomSeed('TCPDF'.$orientation.$unit.$serformat.$encoding));
2119  // set document creation and modification timestamp
2120  $this->doc_creation_timestamp = time();
2121  $this->doc_modification_timestamp = $this->doc_creation_timestamp;
2122  // get default graphic vars
2123  $this->default_graphic_vars = $this->getGraphicVars();
2124  $this->header_xobj_autoreset = false;
2125  $this->custom_xmp = '';
2126  }
2127 
2133  public function __destruct() {
2134  // restore internal encoding
2135  if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) {
2136  mb_internal_encoding($this->internal_encoding);
2137  }
2138  // unset all class variables
2139  $this->_destroy(true);
2140  }
2141 
2148  public function getTCPDFVersion() {
2149  return $this->tcpdf_version;
2150  }
2151 
2158  public function setPageUnit($unit) {
2159  $unit = strtolower($unit);
2160  //Set scale factor
2161  switch ($unit) {
2162  // points
2163  case 'px':
2164  case 'pt': {
2165  $this->k = 1;
2166  break;
2167  }
2168  // millimeters
2169  case 'mm': {
2170  $this->k = $this->dpi / 25.4;
2171  break;
2172  }
2173  // centimeters
2174  case 'cm': {
2175  $this->k = $this->dpi / 2.54;
2176  break;
2177  }
2178  // inches
2179  case 'in': {
2180  $this->k = $this->dpi;
2181  break;
2182  }
2183  // unsupported unit
2184  default : {
2185  $this->Error('Incorrect unit: '.$unit);
2186  break;
2187  }
2188  }
2189  $this->pdfunit = $unit;
2190  if (isset($this->CurOrientation)) {
2191  $this->setPageOrientation($this->CurOrientation);
2192  }
2193  }
2194 
2509  public function getPageSizeFromFormat($format) {
2510  // Paper cordinates are calculated in this way: (inches * 72) where (1 inch = 25.4 mm)
2511  switch (strtoupper($format)) {
2512  // ISO 216 A Series + 2 SIS 014711 extensions
2513  case 'A0' : {$pf = array( 2383.937, 3370.394); break;}
2514  case 'A1' : {$pf = array( 1683.780, 2383.937); break;}
2515  case 'A2' : {$pf = array( 1190.551, 1683.780); break;}
2516  case 'A3' : {$pf = array( 841.890, 1190.551); break;}
2517  case 'A4' : {$pf = array( 595.276, 841.890); break;}
2518  case 'A5' : {$pf = array( 419.528, 595.276); break;}
2519  case 'A6' : {$pf = array( 297.638, 419.528); break;}
2520  case 'A7' : {$pf = array( 209.764, 297.638); break;}
2521  case 'A8' : {$pf = array( 147.402, 209.764); break;}
2522  case 'A9' : {$pf = array( 104.882, 147.402); break;}
2523  case 'A10': {$pf = array( 73.701, 104.882); break;}
2524  case 'A11': {$pf = array( 51.024, 73.701); break;}
2525  case 'A12': {$pf = array( 36.850, 51.024); break;}
2526  // ISO 216 B Series + 2 SIS 014711 extensions
2527  case 'B0' : {$pf = array( 2834.646, 4008.189); break;}
2528  case 'B1' : {$pf = array( 2004.094, 2834.646); break;}
2529  case 'B2' : {$pf = array( 1417.323, 2004.094); break;}
2530  case 'B3' : {$pf = array( 1000.630, 1417.323); break;}
2531  case 'B4' : {$pf = array( 708.661, 1000.630); break;}
2532  case 'B5' : {$pf = array( 498.898, 708.661); break;}
2533  case 'B6' : {$pf = array( 354.331, 498.898); break;}
2534  case 'B7' : {$pf = array( 249.449, 354.331); break;}
2535  case 'B8' : {$pf = array( 175.748, 249.449); break;}
2536  case 'B9' : {$pf = array( 124.724, 175.748); break;}
2537  case 'B10': {$pf = array( 87.874, 124.724); break;}
2538  case 'B11': {$pf = array( 62.362, 87.874); break;}
2539  case 'B12': {$pf = array( 42.520, 62.362); break;}
2540  // ISO 216 C Series + 2 SIS 014711 extensions + 2 EXTENSION
2541  case 'C0' : {$pf = array( 2599.370, 3676.535); break;}
2542  case 'C1' : {$pf = array( 1836.850, 2599.370); break;}
2543  case 'C2' : {$pf = array( 1298.268, 1836.850); break;}
2544  case 'C3' : {$pf = array( 918.425, 1298.268); break;}
2545  case 'C4' : {$pf = array( 649.134, 918.425); break;}
2546  case 'C5' : {$pf = array( 459.213, 649.134); break;}
2547  case 'C6' : {$pf = array( 323.150, 459.213); break;}
2548  case 'C7' : {$pf = array( 229.606, 323.150); break;}
2549  case 'C8' : {$pf = array( 161.575, 229.606); break;}
2550  case 'C9' : {$pf = array( 113.386, 161.575); break;}
2551  case 'C10': {$pf = array( 79.370, 113.386); break;}
2552  case 'C11': {$pf = array( 56.693, 79.370); break;}
2553  case 'C12': {$pf = array( 39.685, 56.693); break;}
2554  case 'C76': {$pf = array( 229.606, 459.213); break;}
2555  case 'DL' : {$pf = array( 311.811, 623.622); break;}
2556  // SIS 014711 E Series
2557  case 'E0' : {$pf = array( 2491.654, 3517.795); break;}
2558  case 'E1' : {$pf = array( 1757.480, 2491.654); break;}
2559  case 'E2' : {$pf = array( 1247.244, 1757.480); break;}
2560  case 'E3' : {$pf = array( 878.740, 1247.244); break;}
2561  case 'E4' : {$pf = array( 623.622, 878.740); break;}
2562  case 'E5' : {$pf = array( 439.370, 623.622); break;}
2563  case 'E6' : {$pf = array( 311.811, 439.370); break;}
2564  case 'E7' : {$pf = array( 221.102, 311.811); break;}
2565  case 'E8' : {$pf = array( 155.906, 221.102); break;}
2566  case 'E9' : {$pf = array( 110.551, 155.906); break;}
2567  case 'E10': {$pf = array( 76.535, 110.551); break;}
2568  case 'E11': {$pf = array( 53.858, 76.535); break;}
2569  case 'E12': {$pf = array( 36.850, 53.858); break;}
2570  // SIS 014711 G Series
2571  case 'G0' : {$pf = array( 2715.591, 3838.110); break;}
2572  case 'G1' : {$pf = array( 1919.055, 2715.591); break;}
2573  case 'G2' : {$pf = array( 1357.795, 1919.055); break;}
2574  case 'G3' : {$pf = array( 958.110, 1357.795); break;}
2575  case 'G4' : {$pf = array( 677.480, 958.110); break;}
2576  case 'G5' : {$pf = array( 479.055, 677.480); break;}
2577  case 'G6' : {$pf = array( 337.323, 479.055); break;}
2578  case 'G7' : {$pf = array( 238.110, 337.323); break;}
2579  case 'G8' : {$pf = array( 167.244, 238.110); break;}
2580  case 'G9' : {$pf = array( 119.055, 167.244); break;}
2581  case 'G10': {$pf = array( 82.205, 119.055); break;}
2582  case 'G11': {$pf = array( 59.528, 82.205); break;}
2583  case 'G12': {$pf = array( 39.685, 59.528); break;}
2584  // ISO Press
2585  case 'RA0': {$pf = array( 2437.795, 3458.268); break;}
2586  case 'RA1': {$pf = array( 1729.134, 2437.795); break;}
2587  case 'RA2': {$pf = array( 1218.898, 1729.134); break;}
2588  case 'RA3': {$pf = array( 864.567, 1218.898); break;}
2589  case 'RA4': {$pf = array( 609.449, 864.567); break;}
2590  case 'SRA0': {$pf = array( 2551.181, 3628.346); break;}
2591  case 'SRA1': {$pf = array( 1814.173, 2551.181); break;}
2592  case 'SRA2': {$pf = array( 1275.591, 1814.173); break;}
2593  case 'SRA3': {$pf = array( 907.087, 1275.591); break;}
2594  case 'SRA4': {$pf = array( 637.795, 907.087); break;}
2595  // German DIN 476
2596  case '4A0': {$pf = array( 4767.874, 6740.787); break;}
2597  case '2A0': {$pf = array( 3370.394, 4767.874); break;}
2598  // Variations on the ISO Standard
2599  case 'A2_EXTRA' : {$pf = array( 1261.417, 1754.646); break;}
2600  case 'A3+' : {$pf = array( 932.598, 1369.134); break;}
2601  case 'A3_EXTRA' : {$pf = array( 912.756, 1261.417); break;}
2602  case 'A3_SUPER' : {$pf = array( 864.567, 1440.000); break;}
2603  case 'SUPER_A3' : {$pf = array( 864.567, 1380.472); break;}
2604  case 'A4_EXTRA' : {$pf = array( 666.142, 912.756); break;}
2605  case 'A4_SUPER' : {$pf = array( 649.134, 912.756); break;}
2606  case 'SUPER_A4' : {$pf = array( 643.465, 1009.134); break;}
2607  case 'A4_LONG' : {$pf = array( 595.276, 986.457); break;}
2608  case 'F4' : {$pf = array( 595.276, 935.433); break;}
2609  case 'SO_B5_EXTRA': {$pf = array( 572.598, 782.362); break;}
2610  case 'A5_EXTRA' : {$pf = array( 490.394, 666.142); break;}
2611  // ANSI Series
2612  case 'ANSI_E': {$pf = array( 2448.000, 3168.000); break;}
2613  case 'ANSI_D': {$pf = array( 1584.000, 2448.000); break;}
2614  case 'ANSI_C': {$pf = array( 1224.000, 1584.000); break;}
2615  case 'ANSI_B': {$pf = array( 792.000, 1224.000); break;}
2616  case 'ANSI_A': {$pf = array( 612.000, 792.000); break;}
2617  // Traditional 'Loose' North American Paper Sizes
2618  case 'USLEDGER':
2619  case 'LEDGER' : {$pf = array( 1224.000, 792.000); break;}
2620  case 'ORGANIZERK':
2621  case 'BIBLE':
2622  case 'USTABLOID':
2623  case 'TABLOID': {$pf = array( 792.000, 1224.000); break;}
2624  case 'ORGANIZERM':
2625  case 'USLETTER':
2626  case 'LETTER' : {$pf = array( 612.000, 792.000); break;}
2627  case 'USLEGAL':
2628  case 'LEGAL' : {$pf = array( 612.000, 1008.000); break;}
2629  case 'GOVERNMENTLETTER':
2630  case 'GLETTER': {$pf = array( 576.000, 756.000); break;}
2631  case 'JUNIORLEGAL':
2632  case 'JLEGAL' : {$pf = array( 576.000, 360.000); break;}
2633  // Other North American Paper Sizes
2634  case 'QUADDEMY': {$pf = array( 2520.000, 3240.000); break;}
2635  case 'SUPER_B': {$pf = array( 936.000, 1368.000); break;}
2636  case 'QUARTO': {$pf = array( 648.000, 792.000); break;}
2637  case 'GOVERNMENTLEGAL':
2638  case 'FOLIO': {$pf = array( 612.000, 936.000); break;}
2639  case 'MONARCH':
2640  case 'EXECUTIVE': {$pf = array( 522.000, 756.000); break;}
2641  case 'ORGANIZERL':
2642  case 'STATEMENT':
2643  case 'MEMO': {$pf = array( 396.000, 612.000); break;}
2644  case 'FOOLSCAP': {$pf = array( 595.440, 936.000); break;}
2645  case 'COMPACT': {$pf = array( 306.000, 486.000); break;}
2646  case 'ORGANIZERJ': {$pf = array( 198.000, 360.000); break;}
2647  // Canadian standard CAN 2-9.60M
2648  case 'P1': {$pf = array( 1587.402, 2437.795); break;}
2649  case 'P2': {$pf = array( 1218.898, 1587.402); break;}
2650  case 'P3': {$pf = array( 793.701, 1218.898); break;}
2651  case 'P4': {$pf = array( 609.449, 793.701); break;}
2652  case 'P5': {$pf = array( 396.850, 609.449); break;}
2653  case 'P6': {$pf = array( 303.307, 396.850); break;}
2654  // North American Architectural Sizes
2655  case 'ARCH_E' : {$pf = array( 2592.000, 3456.000); break;}
2656  case 'ARCH_E1': {$pf = array( 2160.000, 3024.000); break;}
2657  case 'ARCH_D' : {$pf = array( 1728.000, 2592.000); break;}
2658  case 'BROADSHEET':
2659  case 'ARCH_C' : {$pf = array( 1296.000, 1728.000); break;}
2660  case 'ARCH_B' : {$pf = array( 864.000, 1296.000); break;}
2661  case 'ARCH_A' : {$pf = array( 648.000, 864.000); break;}
2662  // --- North American Envelope Sizes ---
2663  // - Announcement Envelopes
2664  case 'ANNENV_A2' : {$pf = array( 314.640, 414.000); break;}
2665  case 'ANNENV_A6' : {$pf = array( 342.000, 468.000); break;}
2666  case 'ANNENV_A7' : {$pf = array( 378.000, 522.000); break;}
2667  case 'ANNENV_A8' : {$pf = array( 396.000, 584.640); break;}
2668  case 'ANNENV_A10' : {$pf = array( 450.000, 692.640); break;}
2669  case 'ANNENV_SLIM': {$pf = array( 278.640, 638.640); break;}
2670  // - Commercial Envelopes
2671  case 'COMMENV_N6_1/4': {$pf = array( 252.000, 432.000); break;}
2672  case 'COMMENV_N6_3/4': {$pf = array( 260.640, 468.000); break;}
2673  case 'COMMENV_N8' : {$pf = array( 278.640, 540.000); break;}
2674  case 'COMMENV_N9' : {$pf = array( 278.640, 638.640); break;}
2675  case 'COMMENV_N10' : {$pf = array( 296.640, 684.000); break;}
2676  case 'COMMENV_N11' : {$pf = array( 324.000, 746.640); break;}
2677  case 'COMMENV_N12' : {$pf = array( 342.000, 792.000); break;}
2678  case 'COMMENV_N14' : {$pf = array( 360.000, 828.000); break;}
2679  // - Catalogue Envelopes
2680  case 'CATENV_N1' : {$pf = array( 432.000, 648.000); break;}
2681  case 'CATENV_N1_3/4' : {$pf = array( 468.000, 684.000); break;}
2682  case 'CATENV_N2' : {$pf = array( 468.000, 720.000); break;}
2683  case 'CATENV_N3' : {$pf = array( 504.000, 720.000); break;}
2684  case 'CATENV_N6' : {$pf = array( 540.000, 756.000); break;}
2685  case 'CATENV_N7' : {$pf = array( 576.000, 792.000); break;}
2686  case 'CATENV_N8' : {$pf = array( 594.000, 810.000); break;}
2687  case 'CATENV_N9_1/2' : {$pf = array( 612.000, 756.000); break;}
2688  case 'CATENV_N9_3/4' : {$pf = array( 630.000, 810.000); break;}
2689  case 'CATENV_N10_1/2': {$pf = array( 648.000, 864.000); break;}
2690  case 'CATENV_N12_1/2': {$pf = array( 684.000, 900.000); break;}
2691  case 'CATENV_N13_1/2': {$pf = array( 720.000, 936.000); break;}
2692  case 'CATENV_N14_1/4': {$pf = array( 810.000, 882.000); break;}
2693  case 'CATENV_N14_1/2': {$pf = array( 828.000, 1044.000); break;}
2694  // Japanese (JIS P 0138-61) Standard B-Series
2695  case 'JIS_B0' : {$pf = array( 2919.685, 4127.244); break;}
2696  case 'JIS_B1' : {$pf = array( 2063.622, 2919.685); break;}
2697  case 'JIS_B2' : {$pf = array( 1459.843, 2063.622); break;}
2698  case 'JIS_B3' : {$pf = array( 1031.811, 1459.843); break;}
2699  case 'JIS_B4' : {$pf = array( 728.504, 1031.811); break;}
2700  case 'JIS_B5' : {$pf = array( 515.906, 728.504); break;}
2701  case 'JIS_B6' : {$pf = array( 362.835, 515.906); break;}
2702  case 'JIS_B7' : {$pf = array( 257.953, 362.835); break;}
2703  case 'JIS_B8' : {$pf = array( 181.417, 257.953); break;}
2704  case 'JIS_B9' : {$pf = array( 127.559, 181.417); break;}
2705  case 'JIS_B10': {$pf = array( 90.709, 127.559); break;}
2706  case 'JIS_B11': {$pf = array( 62.362, 90.709); break;}
2707  case 'JIS_B12': {$pf = array( 45.354, 62.362); break;}
2708  // PA Series
2709  case 'PA0' : {$pf = array( 2381.102, 3174.803,); break;}
2710  case 'PA1' : {$pf = array( 1587.402, 2381.102); break;}
2711  case 'PA2' : {$pf = array( 1190.551, 1587.402); break;}
2712  case 'PA3' : {$pf = array( 793.701, 1190.551); break;}
2713  case 'PA4' : {$pf = array( 595.276, 793.701); break;}
2714  case 'PA5' : {$pf = array( 396.850, 595.276); break;}
2715  case 'PA6' : {$pf = array( 297.638, 396.850); break;}
2716  case 'PA7' : {$pf = array( 198.425, 297.638); break;}
2717  case 'PA8' : {$pf = array( 147.402, 198.425); break;}
2718  case 'PA9' : {$pf = array( 99.213, 147.402); break;}
2719  case 'PA10': {$pf = array( 73.701, 99.213); break;}
2720  // Standard Photographic Print Sizes
2721  case 'PASSPORT_PHOTO': {$pf = array( 99.213, 127.559); break;}
2722  case 'E' : {$pf = array( 233.858, 340.157); break;}
2723  case 'L':
2724  case '3R' : {$pf = array( 252.283, 360.000); break;}
2725  case 'KG':
2726  case '4R' : {$pf = array( 289.134, 430.866); break;}
2727  case '4D' : {$pf = array( 340.157, 430.866); break;}
2728  case '2L':
2729  case '5R' : {$pf = array( 360.000, 504.567); break;}
2730  case '8P':
2731  case '6R' : {$pf = array( 430.866, 575.433); break;}
2732  case '6P':
2733  case '8R' : {$pf = array( 575.433, 720.000); break;}
2734  case '6PW':
2735  case 'S8R' : {$pf = array( 575.433, 864.567); break;}
2736  case '4P':
2737  case '10R' : {$pf = array( 720.000, 864.567); break;}
2738  case '4PW':
2739  case 'S10R': {$pf = array( 720.000, 1080.000); break;}
2740  case '11R' : {$pf = array( 790.866, 1009.134); break;}
2741  case 'S11R': {$pf = array( 790.866, 1224.567); break;}
2742  case '12R' : {$pf = array( 864.567, 1080.000); break;}
2743  case 'S12R': {$pf = array( 864.567, 1292.598); break;}
2744  // Common Newspaper Sizes
2745  case 'NEWSPAPER_BROADSHEET': {$pf = array( 2125.984, 1700.787); break;}
2746  case 'NEWSPAPER_BERLINER' : {$pf = array( 1332.283, 892.913); break;}
2747  case 'NEWSPAPER_TABLOID':
2748  case 'NEWSPAPER_COMPACT' : {$pf = array( 1218.898, 793.701); break;}
2749  // Business Cards
2750  case 'CREDIT_CARD':
2751  case 'BUSINESS_CARD':
2752  case 'BUSINESS_CARD_ISO7810': {$pf = array( 153.014, 242.646); break;}
2753  case 'BUSINESS_CARD_ISO216' : {$pf = array( 147.402, 209.764); break;}
2754  case 'BUSINESS_CARD_IT':
2755  case 'BUSINESS_CARD_UK':
2756  case 'BUSINESS_CARD_FR':
2757  case 'BUSINESS_CARD_DE':
2758  case 'BUSINESS_CARD_ES' : {$pf = array( 155.906, 240.945); break;}
2759  case 'BUSINESS_CARD_CA':
2760  case 'BUSINESS_CARD_US' : {$pf = array( 144.567, 252.283); break;}
2761  case 'BUSINESS_CARD_JP' : {$pf = array( 155.906, 257.953); break;}
2762  case 'BUSINESS_CARD_HK' : {$pf = array( 153.071, 255.118); break;}
2763  case 'BUSINESS_CARD_AU':
2764  case 'BUSINESS_CARD_DK':
2765  case 'BUSINESS_CARD_SE' : {$pf = array( 155.906, 255.118); break;}
2766  case 'BUSINESS_CARD_RU':
2767  case 'BUSINESS_CARD_CZ':
2768  case 'BUSINESS_CARD_FI':
2769  case 'BUSINESS_CARD_HU':
2770  case 'BUSINESS_CARD_IL' : {$pf = array( 141.732, 255.118); break;}
2771  // Billboards
2772  case '4SHEET' : {$pf = array( 2880.000, 4320.000); break;}
2773  case '6SHEET' : {$pf = array( 3401.575, 5102.362); break;}
2774  case '12SHEET': {$pf = array( 8640.000, 4320.000); break;}
2775  case '16SHEET': {$pf = array( 5760.000, 8640.000); break;}
2776  case '32SHEET': {$pf = array(11520.000, 8640.000); break;}
2777  case '48SHEET': {$pf = array(17280.000, 8640.000); break;}
2778  case '64SHEET': {$pf = array(23040.000, 8640.000); break;}
2779  case '96SHEET': {$pf = array(34560.000, 8640.000); break;}
2780  // Old European Sizes
2781  // - Old Imperial English Sizes
2782  case 'EN_EMPEROR' : {$pf = array( 3456.000, 5184.000); break;}
2783  case 'EN_ANTIQUARIAN' : {$pf = array( 2232.000, 3816.000); break;}
2784  case 'EN_GRAND_EAGLE' : {$pf = array( 2070.000, 3024.000); break;}
2785  case 'EN_DOUBLE_ELEPHANT' : {$pf = array( 1926.000, 2880.000); break;}
2786  case 'EN_ATLAS' : {$pf = array( 1872.000, 2448.000); break;}
2787  case 'EN_COLOMBIER' : {$pf = array( 1692.000, 2484.000); break;}
2788  case 'EN_ELEPHANT' : {$pf = array( 1656.000, 2016.000); break;}
2789  case 'EN_DOUBLE_DEMY' : {$pf = array( 1620.000, 2556.000); break;}
2790  case 'EN_IMPERIAL' : {$pf = array( 1584.000, 2160.000); break;}
2791  case 'EN_PRINCESS' : {$pf = array( 1548.000, 2016.000); break;}
2792  case 'EN_CARTRIDGE' : {$pf = array( 1512.000, 1872.000); break;}
2793  case 'EN_DOUBLE_LARGE_POST': {$pf = array( 1512.000, 2376.000); break;}
2794  case 'EN_ROYAL' : {$pf = array( 1440.000, 1800.000); break;}
2795  case 'EN_SHEET':
2796  case 'EN_HALF_POST' : {$pf = array( 1404.000, 1692.000); break;}
2797  case 'EN_SUPER_ROYAL' : {$pf = array( 1368.000, 1944.000); break;}
2798  case 'EN_DOUBLE_POST' : {$pf = array( 1368.000, 2196.000); break;}
2799  case 'EN_MEDIUM' : {$pf = array( 1260.000, 1656.000); break;}
2800  case 'EN_DEMY' : {$pf = array( 1260.000, 1620.000); break;}
2801  case 'EN_LARGE_POST' : {$pf = array( 1188.000, 1512.000); break;}
2802  case 'EN_COPY_DRAUGHT' : {$pf = array( 1152.000, 1440.000); break;}
2803  case 'EN_POST' : {$pf = array( 1116.000, 1386.000); break;}
2804  case 'EN_CROWN' : {$pf = array( 1080.000, 1440.000); break;}
2805  case 'EN_PINCHED_POST' : {$pf = array( 1062.000, 1332.000); break;}
2806  case 'EN_BRIEF' : {$pf = array( 972.000, 1152.000); break;}
2807  case 'EN_FOOLSCAP' : {$pf = array( 972.000, 1224.000); break;}
2808  case 'EN_SMALL_FOOLSCAP' : {$pf = array( 954.000, 1188.000); break;}
2809  case 'EN_POTT' : {$pf = array( 900.000, 1080.000); break;}
2810  // - Old Imperial Belgian Sizes
2811  case 'BE_GRAND_AIGLE' : {$pf = array( 1984.252, 2948.031); break;}
2812  case 'BE_COLOMBIER' : {$pf = array( 1757.480, 2409.449); break;}
2813  case 'BE_DOUBLE_CARRE': {$pf = array( 1757.480, 2607.874); break;}
2814  case 'BE_ELEPHANT' : {$pf = array( 1746.142, 2182.677); break;}
2815  case 'BE_PETIT_AIGLE' : {$pf = array( 1700.787, 2381.102); break;}
2816  case 'BE_GRAND_JESUS' : {$pf = array( 1559.055, 2069.291); break;}
2817  case 'BE_JESUS' : {$pf = array( 1530.709, 2069.291); break;}
2818  case 'BE_RAISIN' : {$pf = array( 1417.323, 1842.520); break;}
2819  case 'BE_GRAND_MEDIAN': {$pf = array( 1303.937, 1714.961); break;}
2820  case 'BE_DOUBLE_POSTE': {$pf = array( 1233.071, 1601.575); break;}
2821  case 'BE_COQUILLE' : {$pf = array( 1218.898, 1587.402); break;}
2822  case 'BE_PETIT_MEDIAN': {$pf = array( 1176.378, 1502.362); break;}
2823  case 'BE_RUCHE' : {$pf = array( 1020.472, 1303.937); break;}
2824  case 'BE_PROPATRIA' : {$pf = array( 977.953, 1218.898); break;}
2825  case 'BE_LYS' : {$pf = array( 898.583, 1125.354); break;}
2826  case 'BE_POT' : {$pf = array( 870.236, 1088.504); break;}
2827  case 'BE_ROSETTE' : {$pf = array( 765.354, 983.622); break;}
2828  // - Old Imperial French Sizes
2829  case 'FR_UNIVERS' : {$pf = array( 2834.646, 3685.039); break;}
2830  case 'FR_DOUBLE_COLOMBIER' : {$pf = array( 2551.181, 3571.654); break;}
2831  case 'FR_GRANDE_MONDE' : {$pf = array( 2551.181, 3571.654); break;}
2832  case 'FR_DOUBLE_SOLEIL' : {$pf = array( 2267.717, 3401.575); break;}
2833  case 'FR_DOUBLE_JESUS' : {$pf = array( 2154.331, 3174.803); break;}
2834  case 'FR_GRAND_AIGLE' : {$pf = array( 2125.984, 3004.724); break;}
2835  case 'FR_PETIT_AIGLE' : {$pf = array( 1984.252, 2664.567); break;}
2836  case 'FR_DOUBLE_RAISIN' : {$pf = array( 1842.520, 2834.646); break;}
2837  case 'FR_JOURNAL' : {$pf = array( 1842.520, 2664.567); break;}
2838  case 'FR_COLOMBIER_AFFICHE': {$pf = array( 1785.827, 2551.181); break;}
2839  case 'FR_DOUBLE_CAVALIER' : {$pf = array( 1757.480, 2607.874); break;}
2840  case 'FR_CLOCHE' : {$pf = array( 1700.787, 2267.717); break;}
2841  case 'FR_SOLEIL' : {$pf = array( 1700.787, 2267.717); break;}
2842  case 'FR_DOUBLE_CARRE' : {$pf = array( 1587.402, 2551.181); break;}
2843  case 'FR_DOUBLE_COQUILLE' : {$pf = array( 1587.402, 2494.488); break;}
2844  case 'FR_JESUS' : {$pf = array( 1587.402, 2154.331); break;}
2845  case 'FR_RAISIN' : {$pf = array( 1417.323, 1842.520); break;}
2846  case 'FR_CAVALIER' : {$pf = array( 1303.937, 1757.480); break;}
2847  case 'FR_DOUBLE_COURONNE' : {$pf = array( 1303.937, 2040.945); break;}
2848  case 'FR_CARRE' : {$pf = array( 1275.591, 1587.402); break;}
2849  case 'FR_COQUILLE' : {$pf = array( 1247.244, 1587.402); break;}
2850  case 'FR_DOUBLE_TELLIERE' : {$pf = array( 1247.244, 1927.559); break;}
2851  case 'FR_DOUBLE_CLOCHE' : {$pf = array( 1133.858, 1700.787); break;}
2852  case 'FR_DOUBLE_POT' : {$pf = array( 1133.858, 1757.480); break;}
2853  case 'FR_ECU' : {$pf = array( 1133.858, 1474.016); break;}
2854  case 'FR_COURONNE' : {$pf = array( 1020.472, 1303.937); break;}
2855  case 'FR_TELLIERE' : {$pf = array( 963.780, 1247.244); break;}
2856  case 'FR_POT' : {$pf = array( 878.740, 1133.858); break;}
2857  // DEFAULT ISO A4
2858  default: {$pf = array( 595.276, 841.890); break;}
2859  }
2860  return $pf;
2861  }
2862 
2918  protected function setPageFormat($format, $orientation='P') {
2919  if (!empty($format) AND isset($this->pagedim[$this->page])) {
2920  // remove inherited values
2921  unset($this->pagedim[$this->page]);
2922  }
2923  if (is_string($format)) {
2924  // get page measures from format name
2925  $pf = $this->getPageSizeFromFormat($format);
2926  $this->fwPt = $pf[0];
2927  $this->fhPt = $pf[1];
2928  } else {
2929  // the boundaries of the physical medium on which the page shall be displayed or printed
2930  if (isset($format['MediaBox'])) {
2931  $this->setPageBoxes($this->page, 'MediaBox', $format['MediaBox']['llx'], $format['MediaBox']['lly'], $format['MediaBox']['urx'], $format['MediaBox']['ury'], false);
2932  $this->fwPt = (($format['MediaBox']['urx'] - $format['MediaBox']['llx']) * $this->k);
2933  $this->fhPt = (($format['MediaBox']['ury'] - $format['MediaBox']['lly']) * $this->k);
2934  } else {
2935  if (isset($format[0]) AND is_numeric($format[0]) AND isset($format[1]) AND is_numeric($format[1])) {
2936  $pf = array(($format[0] * $this->k), ($format[1] * $this->k));
2937  } else {
2938  if (!isset($format['format'])) {
2939  // default value
2940  $format['format'] = 'A4';
2941  }
2942  $pf = $this->getPageSizeFromFormat($format['format']);
2943  }
2944  $this->fwPt = $pf[0];
2945  $this->fhPt = $pf[1];
2946  $this->setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true);
2947  }
2948  // the visible region of default user space
2949  if (isset($format['CropBox'])) {
2950  $this->setPageBoxes($this->page, 'CropBox', $format['CropBox']['llx'], $format['CropBox']['lly'], $format['CropBox']['urx'], $format['CropBox']['ury'], false);
2951  }
2952  // the region to which the contents of the page shall be clipped when output in a production environment
2953  if (isset($format['BleedBox'])) {
2954  $this->setPageBoxes($this->page, 'BleedBox', $format['BleedBox']['llx'], $format['BleedBox']['lly'], $format['BleedBox']['urx'], $format['BleedBox']['ury'], false);
2955  }
2956  // the intended dimensions of the finished page after trimming
2957  if (isset($format['TrimBox'])) {
2958  $this->setPageBoxes($this->page, 'TrimBox', $format['TrimBox']['llx'], $format['TrimBox']['lly'], $format['TrimBox']['urx'], $format['TrimBox']['ury'], false);
2959  }
2960  // the page's meaningful content (including potential white space)
2961  if (isset($format['ArtBox'])) {
2962  $this->setPageBoxes($this->page, 'ArtBox', $format['ArtBox']['llx'], $format['ArtBox']['lly'], $format['ArtBox']['urx'], $format['ArtBox']['ury'], false);
2963  }
2964  // specify the colours and other visual characteristics that should be used in displaying guidelines on the screen for the various page boundaries
2965  if (isset($format['BoxColorInfo'])) {
2966  $this->pagedim[$this->page]['BoxColorInfo'] = $format['BoxColorInfo'];
2967  }
2968  if (isset($format['Rotate']) AND (($format['Rotate'] % 90) == 0)) {
2969  // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.
2970  $this->pagedim[$this->page]['Rotate'] = intval($format['Rotate']);
2971  }
2972  if (isset($format['PZ'])) {
2973  // The page's preferred zoom (magnification) factor
2974  $this->pagedim[$this->page]['PZ'] = floatval($format['PZ']);
2975  }
2976  if (isset($format['trans'])) {
2977  // The style and duration of the visual transition to use when moving from another page to the given page during a presentation
2978  if (isset($format['trans']['Dur'])) {
2979  // The page's display duration
2980  $this->pagedim[$this->page]['trans']['Dur'] = floatval($format['trans']['Dur']);
2981  }
2982  $stansition_styles = array('Split', 'Blinds', 'Box', 'Wipe', 'Dissolve', 'Glitter', 'R', 'Fly', 'Push', 'Cover', 'Uncover', 'Fade');
2983  if (isset($format['trans']['S']) AND in_array($format['trans']['S'], $stansition_styles)) {
2984  // The transition style that shall be used when moving to this page from another during a presentation
2985  $this->pagedim[$this->page]['trans']['S'] = $format['trans']['S'];
2986  $valid_effect = array('Split', 'Blinds');
2987  $valid_vals = array('H', 'V');
2988  if (isset($format['trans']['Dm']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['Dm'], $valid_vals)) {
2989  $this->pagedim[$this->page]['trans']['Dm'] = $format['trans']['Dm'];
2990  }
2991  $valid_effect = array('Split', 'Box', 'Fly');
2992  $valid_vals = array('I', 'O');
2993  if (isset($format['trans']['M']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['M'], $valid_vals)) {
2994  $this->pagedim[$this->page]['trans']['M'] = $format['trans']['M'];
2995  }
2996  $valid_effect = array('Wipe', 'Glitter', 'Fly', 'Cover', 'Uncover', 'Push');
2997  if (isset($format['trans']['Di']) AND in_array($format['trans']['S'], $valid_effect)) {
2998  if (((($format['trans']['Di'] == 90) OR ($format['trans']['Di'] == 180)) AND ($format['trans']['S'] == 'Wipe'))
2999  OR (($format['trans']['Di'] == 315) AND ($format['trans']['S'] == 'Glitter'))
3000  OR (($format['trans']['Di'] == 0) OR ($format['trans']['Di'] == 270))) {
3001  $this->pagedim[$this->page]['trans']['Di'] = intval($format['trans']['Di']);
3002  }
3003  }
3004  if (isset($format['trans']['SS']) AND ($format['trans']['S'] == 'Fly')) {
3005  $this->pagedim[$this->page]['trans']['SS'] = floatval($format['trans']['SS']);
3006  }
3007  if (isset($format['trans']['B']) AND ($format['trans']['B'] === true) AND ($format['trans']['S'] == 'Fly')) {
3008  $this->pagedim[$this->page]['trans']['B'] = 'true';
3009  }
3010  } else {
3011  $this->pagedim[$this->page]['trans']['S'] = 'R';
3012  }
3013  if (isset($format['trans']['D'])) {
3014  // The duration of the transition effect, in seconds
3015  $this->pagedim[$this->page]['trans']['D'] = floatval($format['trans']['D']);
3016  } else {
3017  $this->pagedim[$this->page]['trans']['D'] = 1;
3018  }
3019  }
3020  }
3021  $this->setPageOrientation($orientation);
3022  }
3023 
3036  public function setPageBoxes($page, $type, $llx, $lly, $urx, $ury, $points=false) {
3037  if (!isset($this->pagedim[$page])) {
3038  // initialize array
3039  $this->pagedim[$page] = array();
3040  }
3041  $pageboxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox');
3042  if (!in_array($type, $pageboxes)) {
3043  return;
3044  }
3045  if ($points) {
3046  $k = 1;
3047  } else {
3048  $k = $this->k;
3049  }
3050  $this->pagedim[$page][$type]['llx'] = ($llx * $k);
3051  $this->pagedim[$page][$type]['lly'] = ($lly * $k);
3052  $this->pagedim[$page][$type]['urx'] = ($urx * $k);
3053  $this->pagedim[$page][$type]['ury'] = ($ury * $k);
3054  }
3055 
3062  protected function swapPageBoxCoordinates($page) {
3063  $pageboxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox');
3064  foreach ($pageboxes as $type) {
3065  // swap X and Y coordinates
3066  if (isset($this->pagedim[$page][$type])) {
3067  $tmp = $this->pagedim[$page][$type]['llx'];
3068  $this->pagedim[$page][$type]['llx'] = $this->pagedim[$page][$type]['lly'];
3069  $this->pagedim[$page][$type]['lly'] = $tmp;
3070  $tmp = $this->pagedim[$page][$type]['urx'];
3071  $this->pagedim[$page][$type]['urx'] = $this->pagedim[$page][$type]['ury'];
3072  $this->pagedim[$page][$type]['ury'] = $tmp;
3073  }
3074  }
3075  }
3076 
3085  public function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') {
3086  if (!isset($this->pagedim[$this->page]['MediaBox'])) {
3087  // the boundaries of the physical medium on which the page shall be displayed or printed
3088  $this->setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true);
3089  }
3090  if (!isset($this->pagedim[$this->page]['CropBox'])) {
3091  // the visible region of default user space
3092  $this->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);
3093  }
3094  if (!isset($this->pagedim[$this->page]['BleedBox'])) {
3095  // the region to which the contents of the page shall be clipped when output in a production environment
3096  $this->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);
3097  }
3098  if (!isset($this->pagedim[$this->page]['TrimBox'])) {
3099  // the intended dimensions of the finished page after trimming
3100  $this->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);
3101  }
3102  if (!isset($this->pagedim[$this->page]['ArtBox'])) {
3103  // the page's meaningful content (including potential white space)
3104  $this->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);
3105  }
3106  if (!isset($this->pagedim[$this->page]['Rotate'])) {
3107  // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.
3108  $this->pagedim[$this->page]['Rotate'] = 0;
3109  }
3110  if (!isset($this->pagedim[$this->page]['PZ'])) {
3111  // The page's preferred zoom (magnification) factor
3112  $this->pagedim[$this->page]['PZ'] = 1;
3113  }
3114  if ($this->fwPt > $this->fhPt) {
3115  // landscape
3116  $default_orientation = 'L';
3117  } else {
3118  // portrait
3119  $default_orientation = 'P';
3120  }
3121  $valid_orientations = array('P', 'L');
3122  if (empty($orientation)) {
3123  $orientation = $default_orientation;
3124  } else {
3125  $orientation = strtoupper($orientation{0});
3126  }
3127  if (in_array($orientation, $valid_orientations) AND ($orientation != $default_orientation)) {
3128  $this->CurOrientation = $orientation;
3129  $this->wPt = $this->fhPt;
3130  $this->hPt = $this->fwPt;
3131  } else {
3132  $this->CurOrientation = $default_orientation;
3133  $this->wPt = $this->fwPt;
3134  $this->hPt = $this->fhPt;
3135  }
3136  if ((abs($this->pagedim[$this->page]['MediaBox']['urx'] - $this->hPt) < $this->feps) AND (abs($this->pagedim[$this->page]['MediaBox']['ury'] - $this->wPt) < $this->feps)){
3137  // swap X and Y coordinates (change page orientation)
3138  $this->swapPageBoxCoordinates($this->page);
3139  }
3140  $this->w = ($this->wPt / $this->k);
3141  $this->h = ($this->hPt / $this->k);
3142  if ($this->empty_string($autopagebreak)) {
3143  if (isset($this->AutoPageBreak)) {
3144  $autopagebreak = $this->AutoPageBreak;
3145  } else {
3146  $autopagebreak = true;
3147  }
3148  }
3149  if ($this->empty_string($bottommargin)) {
3150  if (isset($this->bMargin)) {
3151  $bottommargin = $this->bMargin;
3152  } else {
3153  // default value = 2 cm
3154  $bottommargin = 2 * 28.35 / $this->k;
3155  }
3156  }
3157  $this->SetAutoPageBreak($autopagebreak, $bottommargin);
3158  // store page dimensions
3159  $this->pagedim[$this->page]['w'] = $this->wPt;
3160  $this->pagedim[$this->page]['h'] = $this->hPt;
3161  $this->pagedim[$this->page]['wk'] = $this->w;
3162  $this->pagedim[$this->page]['hk'] = $this->h;
3163  $this->pagedim[$this->page]['tm'] = $this->tMargin;
3164  $this->pagedim[$this->page]['bm'] = $bottommargin;
3165  $this->pagedim[$this->page]['lm'] = $this->lMargin;
3166  $this->pagedim[$this->page]['rm'] = $this->rMargin;
3167  $this->pagedim[$this->page]['pb'] = $autopagebreak;
3168  $this->pagedim[$this->page]['or'] = $this->CurOrientation;
3169  $this->pagedim[$this->page]['olm'] = $this->original_lMargin;
3170  $this->pagedim[$this->page]['orm'] = $this->original_rMargin;
3171  }
3172 
3190  public function setSpacesRE($re='/[^\S\xa0]/') {
3191  $this->re_spaces = $re;
3192  $re_parts = explode('/', $re);
3193  // get pattern parts
3194  $this->re_space = array();
3195  if (isset($re_parts[1]) AND !empty($re_parts[1])) {
3196  $this->re_space['p'] = $re_parts[1];
3197  } else {
3198  $this->re_space['p'] = '[\s]';
3199  }
3200  // set pattern modifiers
3201  if (isset($re_parts[2]) AND !empty($re_parts[2])) {
3202  $this->re_space['m'] = $re_parts[2];
3203  } else {
3204  $this->re_space['m'] = '';
3205  }
3206  }
3207 
3215  public function setRTL($enable, $resetx=true) {
3216  $enable = $enable ? true : false;
3217  $resetx = ($resetx AND ($enable != $this->rtl));
3218  $this->rtl = $enable;
3219  $this->tmprtl = false;
3220  if ($resetx) {
3221  $this->Ln(0);
3222  }
3223  }
3224 
3231  public function getRTL() {
3232  return $this->rtl;
3233  }
3234 
3241  public function setTempRTL($mode) {
3242  $newmode = false;
3243  switch (strtoupper($mode)) {
3244  case 'LTR':
3245  case 'L': {
3246  if ($this->rtl) {
3247  $newmode = 'L';
3248  }
3249  break;
3250  }
3251  case 'RTL':
3252  case 'R': {
3253  if (!$this->rtl) {
3254  $newmode = 'R';
3255  }
3256  break;
3257  }
3258  case false:
3259  default: {
3260  $newmode = false;
3261  break;
3262  }
3263  }
3264  $this->tmprtl = $newmode;
3265  }
3266 
3273  public function isRTLTextDir() {
3274  return ($this->rtl OR ($this->tmprtl == 'R'));
3275  }
3276 
3284  public function setLastH($h) {
3285  $this->lasth = $h;
3286  }
3287 
3293  public function resetLastH() {
3294  $this->lasth = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
3295  }
3296 
3303  public function getLastH() {
3304  return $this->lasth;
3305  }
3306 
3314  public function setImageScale($scale) {
3315  $this->imgscale = $scale;
3316  }
3317 
3325  public function getImageScale() {
3326  return $this->imgscale;
3327  }
3328 
3338  public function getPageDimensions($pagenum='') {
3339  if (empty($pagenum)) {
3340  $pagenum = $this->page;
3341  }
3342  return $this->pagedim[$pagenum];
3343  }
3344 
3354  public function getPageWidth($pagenum='') {
3355  if (empty($pagenum)) {
3356  return $this->w;
3357  }
3358  return $this->pagedim[$pagenum]['w'];
3359  }
3360 
3370  public function getPageHeight($pagenum='') {
3371  if (empty($pagenum)) {
3372  return $this->h;
3373  }
3374  return $this->pagedim[$pagenum]['h'];
3375  }
3376 
3386  public function getBreakMargin($pagenum='') {
3387  if (empty($pagenum)) {
3388  return $this->bMargin;
3389  }
3390  return $this->pagedim[$pagenum]['bm'];
3391  }
3392 
3400  public function getScaleFactor() {
3401  return $this->k;
3402  }
3403 
3414  public function SetMargins($left, $top, $right=-1, $keepmargins=false) {
3415  //Set left, top and right margins
3416  $this->lMargin = $left;
3417  $this->tMargin = $top;
3418  if ($right == -1) {
3419  $right = $left;
3420  }
3421  $this->rMargin = $right;
3422  if ($keepmargins) {
3423  // overwrite original values
3424  $this->original_lMargin = $this->lMargin;
3425  $this->original_rMargin = $this->rMargin;
3426  }
3427  }
3428 
3436  public function SetLeftMargin($margin) {
3437  //Set left margin
3438  $this->lMargin = $margin;
3439  if (($this->page > 0) AND ($this->x < $margin)) {
3440  $this->x = $margin;
3441  }
3442  }
3443 
3451  public function SetTopMargin($margin) {
3452  //Set top margin
3453  $this->tMargin = $margin;
3454  if (($this->page > 0) AND ($this->y < $margin)) {
3455  $this->y = $margin;
3456  }
3457  }
3458 
3466  public function SetRightMargin($margin) {
3467  $this->rMargin = $margin;
3468  if (($this->page > 0) AND ($this->x > ($this->w - $margin))) {
3469  $this->x = $this->w - $margin;
3470  }
3471  }
3472 
3480  public function SetCellPadding($pad) {
3481  if ($pad >= 0) {
3482  $this->cell_padding['L'] = $pad;
3483  $this->cell_padding['T'] = $pad;
3484  $this->cell_padding['R'] = $pad;
3485  $this->cell_padding['B'] = $pad;
3486  }
3487  }
3488 
3499  public function setCellPaddings($left='', $top='', $right='', $bottom='') {
3500  if (($left !== '') AND ($left >= 0)) {
3501  $this->cell_padding['L'] = $left;
3502  }
3503  if (($top !== '') AND ($top >= 0)) {
3504  $this->cell_padding['T'] = $top;
3505  }
3506  if (($right !== '') AND ($right >= 0)) {
3507  $this->cell_padding['R'] = $right;
3508  }
3509  if (($bottom !== '') AND ($bottom >= 0)) {
3510  $this->cell_padding['B'] = $bottom;
3511  }
3512  }
3513 
3521  public function getCellPaddings() {
3522  return $this->cell_padding;
3523  }
3524 
3535  public function setCellMargins($left='', $top='', $right='', $bottom='') {
3536  if (($left !== '') AND ($left >= 0)) {
3537  $this->cell_margin['L'] = $left;
3538  }
3539  if (($top !== '') AND ($top >= 0)) {
3540  $this->cell_margin['T'] = $top;
3541  }
3542  if (($right !== '') AND ($right >= 0)) {
3543  $this->cell_margin['R'] = $right;
3544  }
3545  if (($bottom !== '') AND ($bottom >= 0)) {
3546  $this->cell_margin['B'] = $bottom;
3547  }
3548  }
3549 
3557  public function getCellMargins() {
3558  return $this->cell_margin;
3559  }
3560 
3568  protected function adjustCellPadding($brd=0) {
3569  if (empty($brd)) {
3570  return;
3571  }
3572  if (is_string($brd)) {
3573  // convert string to array
3574  $slen = strlen($brd);
3575  $newbrd = array();
3576  for ($i = 0; $i < $slen; ++$i) {
3577  $newbrd[$brd[$i]] = true;
3578  }
3579  $brd = $newbrd;
3580  } elseif (($brd === 1) OR ($brd === true) OR (is_numeric($brd) AND (intval($brd) > 0))) {
3581  $brd = array('LRTB' => true);
3582  }
3583  if (!is_array($brd)) {
3584  return;
3585  }
3586  // store current cell padding
3587  $cp = $this->cell_padding;
3588  // select border mode
3589  if (isset($brd['mode'])) {
3590  $mode = $brd['mode'];
3591  unset($brd['mode']);
3592  } else {
3593  $mode = 'normal';
3594  }
3595  // process borders
3596  foreach ($brd as $border => $style) {
3597  $line_width = $this->LineWidth;
3598  if (is_array($style) AND isset($style['width'])) {
3599  // get border width
3600  $line_width = $style['width'];
3601  }
3602  $adj = 0; // line width inside the cell
3603  switch ($mode) {
3604  case 'ext': {
3605  $adj = 0;
3606  break;
3607  }
3608  case 'int': {
3609  $adj = $line_width;
3610  break;
3611  }
3612  case 'normal':
3613  default: {
3614  $adj = ($line_width / 2);
3615  break;
3616  }
3617  }
3618  // correct internal cell padding if required to avoid overlap between text and lines
3619  if ((strpos($border,'T') !== false) AND ($this->cell_padding['T'] < $adj)) {
3620  $this->cell_padding['T'] = $adj;
3621  }
3622  if ((strpos($border,'R') !== false) AND ($this->cell_padding['R'] < $adj)) {
3623  $this->cell_padding['R'] = $adj;
3624  }
3625  if ((strpos($border,'B') !== false) AND ($this->cell_padding['B'] < $adj)) {
3626  $this->cell_padding['B'] = $adj;
3627  }
3628  if ((strpos($border,'L') !== false) AND ($this->cell_padding['L'] < $adj)) {
3629  $this->cell_padding['L'] = $adj;
3630  }
3631  }
3632  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']));
3633  }
3634 
3643  public function SetAutoPageBreak($auto, $margin=0) {
3644  $this->AutoPageBreak = $auto ? true : false;
3645  $this->bMargin = $margin;
3646  $this->PageBreakTrigger = $this->h - ($margin*2);
3647  }
3648 
3655  public function getAutoPageBreak() {
3656  return $this->AutoPageBreak;
3657  }
3658 
3667  public function SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone') {
3668  if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) {
3669  $this->ZoomMode = $zoom;
3670  } else {
3671  $this->Error('Incorrect zoom display mode: '.$zoom);
3672  }
3673  switch ($layout) {
3674  case 'default':
3675  case 'single':
3676  case 'SinglePage': {
3677  $this->LayoutMode = 'SinglePage';
3678  break;
3679  }
3680  case 'continuous':
3681  case 'OneColumn': {
3682  $this->LayoutMode = 'OneColumn';
3683  break;
3684  }
3685  case 'two':
3686  case 'TwoColumnLeft': {
3687  $this->LayoutMode = 'TwoColumnLeft';
3688  break;
3689  }
3690  case 'TwoColumnRight': {
3691  $this->LayoutMode = 'TwoColumnRight';
3692  break;
3693  }
3694  case 'TwoPageLeft': {
3695  $this->LayoutMode = 'TwoPageLeft';
3696  break;
3697  }
3698  case 'TwoPageRight': {
3699  $this->LayoutMode = 'TwoPageRight';
3700  break;
3701  }
3702  default: {
3703  $this->LayoutMode = 'SinglePage';
3704  }
3705  }
3706  switch ($mode) {
3707  case 'UseNone': {
3708  $this->PageMode = 'UseNone';
3709  break;
3710  }
3711  case 'UseOutlines': {
3712  $this->PageMode = 'UseOutlines';
3713  break;
3714  }
3715  case 'UseThumbs': {
3716  $this->PageMode = 'UseThumbs';
3717  break;
3718  }
3719  case 'FullScreen': {
3720  $this->PageMode = 'FullScreen';
3721  break;
3722  }
3723  case 'UseOC': {
3724  $this->PageMode = 'UseOC';
3725  break;
3726  }
3727  case '': {
3728  $this->PageMode = 'UseAttachments';
3729  break;
3730  }
3731  default: {
3732  $this->PageMode = 'UseNone';
3733  }
3734  }
3735  }
3736 
3744  public function SetCompression($compress=true) {
3745  if (function_exists('gzcompress')) {
3746  $this->compress = $compress ? true : false;
3747  } else {
3748  $this->compress = false;
3749  }
3750  }
3751 
3758  public function setSRGBmode($mode=false) {
3759  $this->force_srgb = $mode ? true : false;
3760  }
3761 
3769  public function SetDocInfoUnicode($unicode=true) {
3770  $this->docinfounicode = $unicode ? true : false;
3771  }
3772 
3780  public function SetTitle($title) {
3781  $this->title = $title;
3782  }
3783 
3791  public function SetSubject($subject) {
3792  $this->subject = $subject;
3793  }
3794 
3802  public function SetAuthor($author) {
3803  $this->author = $author;
3804  }
3805 
3813  public function SetKeywords($keywords) {
3814  $this->keywords = $keywords;
3815  }
3816 
3824  public function SetCreator($creator) {
3825  $this->creator = $creator;
3826  }
3827 
3835  public function Error($msg) {
3836  // unset all class variables
3837  $this->_destroy(true);
3838  // exit program and print error
3839  die('<strong>TCPDF ERROR: </strong>'.$msg);
3840  }
3841 
3850  public function Open() {
3851  $this->state = 1;
3852  }
3853 
3862  public function Close() {
3863  if ($this->state == 3) {
3864  return;
3865  }
3866  if ($this->page == 0) {
3867  $this->AddPage();
3868  }
3869  $this->endLayer();
3870  if ($this->tcpdflink) {
3871  // save current graphic settings
3872  $gvars = $this->getGraphicVars();
3873  $this->setEqualColumns();
3874  $this->lastpage(true);
3875  $this->SetAutoPageBreak(false);
3876  $this->x = 0;
3877  $this->y = $this->h - (1 / $this->k);
3878  $this->lMargin = 0;
3879  $this->_out('q');
3880  $font = defined('PDF_FONT_NAME_MAIN')?PDF_FONT_NAME_MAIN:'helvetica';
3881  $this->SetFont($font, '', 1);
3882  $this->setTextRenderingMode(0, false, false);
3883  $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";
3884  $lnk = "\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67";
3885  $this->Cell(0, 0, $msg, 0, 0, 'L', 0, $lnk, 0, false, 'D', 'B');
3886  $this->_out('Q');
3887  // restore graphic settings
3888  $this->setGraphicVars($gvars);
3889  }
3890  // close page
3891  $this->endPage();
3892  // close document
3893  $this->_enddoc();
3894  // unset all class variables (except critical ones)
3895  $this->_destroy(false);
3896  }
3897 
3906  public function setPage($pnum, $resetmargins=false) {
3907  if (($pnum == $this->page) AND ($this->state == 2)) {
3908  return;
3909  }
3910  if (($pnum > 0) AND ($pnum <= $this->numpages)) {
3911  $this->state = 2;
3912  // save current graphic settings
3913  //$gvars = $this->getGraphicVars();
3914  $oldpage = $this->page;
3915  $this->page = $pnum;
3916  $this->wPt = $this->pagedim[$this->page]['w'];
3917  $this->hPt = $this->pagedim[$this->page]['h'];
3918  $this->w = $this->pagedim[$this->page]['wk'];
3919  $this->h = $this->pagedim[$this->page]['hk'];
3920  $this->tMargin = $this->pagedim[$this->page]['tm'];
3921  $this->bMargin = $this->pagedim[$this->page]['bm'];
3922  $this->original_lMargin = $this->pagedim[$this->page]['olm'];
3923  $this->original_rMargin = $this->pagedim[$this->page]['orm'];
3924  $this->AutoPageBreak = $this->pagedim[$this->page]['pb'];
3925  $this->CurOrientation = $this->pagedim[$this->page]['or'];
3926  $this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin);
3927  // restore graphic settings
3928  //$this->setGraphicVars($gvars);
3929  if ($resetmargins) {
3930  $this->lMargin = $this->pagedim[$this->page]['olm'];
3931  $this->rMargin = $this->pagedim[$this->page]['orm'];
3932  $this->SetY($this->tMargin);
3933  } else {
3934  // account for booklet mode
3935  if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
3936  $deltam = $this->pagedim[$this->page]['olm'] - $this->pagedim[$this->page]['orm'];
3937  $this->lMargin += $deltam;
3938  $this->rMargin -= $deltam;
3939  }
3940  }
3941  } else {
3942  $this->Error('Wrong page number on setPage() function: '.$pnum);
3943  }
3944  }
3945 
3953  public function lastPage($resetmargins=false) {
3954  $this->setPage($this->getNumPages(), $resetmargins);
3955  }
3956 
3964  public function getPage() {
3965  return $this->page;
3966  }
3967 
3975  public function getNumPages() {
3976  return $this->numpages;
3977  }
3978 
3988  public function addTOCPage($orientation='', $format='', $keepmargins=false) {
3989  $this->AddPage($orientation, $format, $keepmargins, true);
3990  }
3991 
3998  public function endTOCPage() {
3999  $this->endPage(true);
4000  }
4001 
4013  public function AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false) {
4014  if ($this->inxobj) {
4015  // we are inside an XObject template
4016  return;
4017  }
4018  if (!isset($this->original_lMargin) OR $keepmargins) {
4019  $this->original_lMargin = $this->lMargin;
4020  }
4021  if (!isset($this->original_rMargin) OR $keepmargins) {
4022  $this->original_rMargin = $this->rMargin;
4023  }
4024  // terminate previous page
4025  $this->endPage();
4026  // start new page
4027  $this->startPage($orientation, $format, $tocpage);
4028  }
4029 
4037  public function endPage($tocpage=false) {
4038  // check if page is already closed
4039  if (($this->page == 0) OR ($this->numpages > $this->page) OR (!$this->pageopen[$this->page])) {
4040  return;
4041  }
4042  // print page footer
4043  $this->setFooter();
4044  // close page
4045  $this->_endpage();
4046  // mark page as closed
4047  $this->pageopen[$this->page] = false;
4048  if ($tocpage) {
4049  $this->tocpage = false;
4050  }
4051  }
4052 
4063  public function startPage($orientation='', $format='', $tocpage=false) {
4064  if ($tocpage) {
4065  $this->tocpage = true;
4066  }
4067  // move page numbers of documents to be attached
4068  if ($this->tocpage) {
4069  // move reference to unexistent pages (used for page attachments)
4070  // adjust outlines
4071  $tmpoutlines = $this->outlines;
4072  foreach ($tmpoutlines as $key => $outline) {
4073  if ($outline['p'] > $this->numpages) {
4074  $this->outlines[$key]['p'] = ($outline['p'] + 1);
4075  }
4076  }
4077  // adjust dests
4078  $tmpdests = $this->dests;
4079  foreach ($tmpdests as $key => $dest) {
4080  if ($dest['p'] > $this->numpages) {
4081  $this->dests[$key]['p'] = ($dest['p'] + 1);
4082  }
4083  }
4084  // adjust links
4085  $tmplinks = $this->links;
4086  foreach ($tmplinks as $key => $link) {
4087  if ($link[0] > $this->numpages) {
4088  $this->links[$key][0] = ($link[0] + 1);
4089  }
4090  }
4091  }
4092  if ($this->numpages > $this->page) {
4093  // this page has been already added
4094  $this->setPage($this->page + 1);
4095  $this->SetY($this->tMargin);
4096  return;
4097  }
4098  // start a new page
4099  if ($this->state == 0) {
4100  $this->Open();
4101  }
4102  ++$this->numpages;
4103  $this->swapMargins($this->booklet);
4104  // save current graphic settings
4105  $gvars = $this->getGraphicVars();
4106  // start new page
4107  $this->_beginpage($orientation, $format);
4108  // mark page as open
4109  $this->pageopen[$this->page] = true;
4110  // restore graphic settings
4111  $this->setGraphicVars($gvars);
4112  // mark this point
4113  $this->setPageMark();
4114  // print page header
4115  // Désactivation par PEEL : $this->setHeader();
4116  // restore graphic settings
4117  $this->setGraphicVars($gvars);
4118  // mark this point
4119  $this->setPageMark();
4120  // print table header (if any)
4121  $this->setTableHeader();
4122  // set mark for empty page check
4123  $this->emptypagemrk[$this->page]= $this->pagelen[$this->page];
4124  }
4125 
4134  public function setPageMark() {
4135  $this->intmrk[$this->page] = $this->pagelen[$this->page];
4136  $this->bordermrk[$this->page] = $this->intmrk[$this->page];
4137  $this->setContentMark();
4138  }
4139 
4147  protected function setContentMark($page=0) {
4148  if ($page <= 0) {
4149  $page = $this->page;
4150  }
4151  if (isset($this->footerlen[$page])) {
4152  $this->cntmrk[$page] = $this->pagelen[$page] - $this->footerlen[$page];
4153  } else {
4154  $this->cntmrk[$page] = $this->pagelen[$page];
4155  }
4156  }
4157 
4168  public function setHeaderData($ln='', $lw=0, $ht='', $hs='', $tc=array(0,0,0), $lc=array(0,0,0)) {
4169  $this->header_logo = $ln;
4170  $this->header_logo_width = $lw;
4171  $this->header_title = $ht;
4172  $this->header_string = $hs;
4173  $this->header_text_color = $tc;
4174  $this->header_line_color = $lc;
4175  }
4176 
4183  public function setFooterData($tc=array(0,0,0), $lc=array(0,0,0)) {
4184  $this->footer_text_color = $tc;
4185  $this->footer_line_color = $lc;
4186  }
4187 
4195  public function getHeaderData() {
4196  $ret = array();
4197  $ret['logo'] = $this->header_logo;
4198  $ret['logo_width'] = $this->header_logo_width;
4199  $ret['title'] = $this->header_title;
4200  $ret['string'] = $this->header_string;
4201  $ret['text_color'] = $this->header_text_color;
4202  $ret['line_color'] = $this->header_line_color;
4203  return $ret;
4204  }
4205 
4212  public function setHeaderMargin($hm=10) {
4213  $this->header_margin = $hm;
4214  }
4215 
4222  public function getHeaderMargin() {
4223  return $this->header_margin;
4224  }
4225 
4232  public function setFooterMargin($fm=10) {
4233  $this->footer_margin = $fm;
4234  }
4235 
4242  public function getFooterMargin() {
4243  return $this->footer_margin;
4244  }
4250  public function setPrintHeader($val=true) {
4251  $this->print_header = $val ? true : false;
4252  }
4253 
4259  public function setPrintFooter($val=true) {
4260  $this->print_footer = $val ? true : false;
4261  }
4262 
4268  public function getImageRBX() {
4269  return $this->img_rb_x;
4270  }
4271 
4277  public function getImageRBY() {
4278  return $this->img_rb_y;
4279  }
4280 
4285  public function resetHeaderTemplate() {
4286  $this->header_xobjid = -1;
4287  }
4288 
4294  public function setHeaderTemplateAutoreset($val=true) {
4295  $this->header_xobj_autoreset = $val ? true : false;
4296  }
4297 
4303  public function Header() {
4304  if ($this->header_xobjid < 0) {
4305  // start a new XObject Template
4306  $this->header_xobjid = $this->startTemplate($this->w, $this->tMargin);
4307  $headerfont = $this->getHeaderFont();
4308  $headerdata = $this->getHeaderData();
4309  $this->y = $this->header_margin;
4310  if ($this->rtl) {
4311  $this->x = $this->w - $this->original_rMargin;
4312  } else {
4313  $this->x = $this->original_lMargin;
4314  }
4315  if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) {
4316  $imgtype = $this->getImageFileType(K_PATH_IMAGES.$headerdata['logo']);
4317  if (($imgtype == 'eps') OR ($imgtype == 'ai')) {
4318  $this->ImageEps(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
4319  } elseif ($imgtype == 'svg') {
4320  $this->ImageSVG(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
4321  } else {
4322  $this->Image(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
4323  }
4324  $imgy = $this->getImageRBY();
4325  } else {
4326  $imgy = $this->y;
4327  }
4328  $cell_height = round(($this->cell_height_ratio * $headerfont[2]) / $this->k, 2);
4329  // set starting margin for text data cell
4330  if ($this->getRTL()) {
4331  $header_x = $this->original_rMargin + ($headerdata['logo_width'] * 1.1);
4332  } else {
4333  $header_x = $this->original_lMargin + ($headerdata['logo_width'] * 1.1);
4334  }
4335  $cw = $this->w - $this->original_lMargin - $this->original_rMargin - ($headerdata['logo_width'] * 1.1);
4336  $this->SetTextColorArray($this->header_text_color);
4337  // header title
4338  $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1);
4339  $this->SetX($header_x);
4340  $this->Cell($cw, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0);
4341  // header string
4342  $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]);
4343  $this->SetX($header_x);
4344  $this->MultiCell($cw, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false, true, 0, 'T', false);
4345  // print an ending header line
4346  $this->SetLineStyle(array('width' => 0.85 / $this->k, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $headerdata['line_color']));
4347  $this->SetY((2.835 / $this->k) + max($imgy, $this->y));
4348  if ($this->rtl) {
4349  $this->SetX($this->original_rMargin);
4350  } else {
4351  $this->SetX($this->original_lMargin);
4352  }
4353  $this->Cell(($this->w - $this->original_lMargin - $this->original_rMargin), 0, '', 'T', 0, 'C');
4354  $this->endTemplate();
4355  }
4356  // print header template
4357  $x = 0;
4358  $dx = 0;
4359  if (!$this->header_xobj_autoreset AND $this->booklet AND (($this->page % 2) == 0)) {
4360  // adjust margins for booklet mode
4361  $dx = ($this->original_lMargin - $this->original_rMargin);
4362  }
4363  if ($this->rtl) {
4364  $x = $this->w + $dx;
4365  } else {
4366  $x = 0 + $dx;
4367  }
4368  $this->printTemplate($this->header_xobjid, $x, 0, 0, 0, '', '', false);
4369  if ($this->header_xobj_autoreset) {
4370  // reset header xobject template at each page
4371  $this->header_xobjid = -1;
4372  }
4373  }
4374 
4380  public function Footer() {
4381  $cur_y = $this->y;
4382  $this->SetTextColorArray($this->footer_text_color);
4383  //set style for cell border
4384  $line_width = (0.85 / $this->k);
4385  $this->SetLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $this->footer_line_color));
4386  //print document barcode
4387  $barcode = $this->getBarcode();
4388  if (!empty($barcode)) {
4389  $this->Ln($line_width);
4390  $barcode_width = round(($this->w - $this->original_lMargin - $this->original_rMargin) / 3);
4391  $style = array(
4392  'position' => $this->rtl?'R':'L',
4393  'align' => $this->rtl?'R':'L',
4394  'stretch' => false,
4395  'fitwidth' => true,
4396  'cellfitalign' => '',
4397  'border' => false,
4398  'padding' => 0,
4399  'fgcolor' => array(0,0,0),
4400  'bgcolor' => false,
4401  'text' => false
4402  );
4403  $this->write1DBarcode($barcode, 'C128', '', $cur_y + $line_width, '', (($this->footer_margin / 3) - $line_width), 0.3, $style, '');
4404  }
4405  $w_page = isset($this->l['w_page']) ? $this->l['w_page'].' ' : '';
4406  if (empty($this->pagegroups)) {
4407  $pagenumtxt = $w_page.$this->getAliasNumPage().' / '.$this->getAliasNbPages();
4408  } else {
4409  $pagenumtxt = $w_page.$this->getPageNumGroupAlias().' / '.$this->getPageGroupAlias();
4410  }
4411  $this->SetY($cur_y);
4412  //Print page number
4413  if ($this->getRTL()) {
4414  $this->SetX($this->original_rMargin);
4415  $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L');
4416  } else {
4417  $this->SetX($this->original_lMargin);
4418  $this->Cell(0, 0, $this->getAliasRightShift().$pagenumtxt, 'T', 0, 'R');
4419  }
4420  }
4421 
4427  protected function setHeader() {
4428  if (!$this->print_header OR ($this->state != 2)) {
4429  return;
4430  }
4431  $this->InHeader = true;
4432  $this->setGraphicVars($this->default_graphic_vars);
4433  $temp_thead = $this->thead;
4434  $temp_theadMargins = $this->theadMargins;
4435  $lasth = $this->lasth;
4436  $this->_out('q');
4437  $this->rMargin = $this->original_rMargin;
4438  $this->lMargin = $this->original_lMargin;
4439  $this->SetCellPadding(0);
4440  //set current position
4441  if ($this->rtl) {
4442  $this->SetXY($this->original_rMargin, $this->header_margin);
4443  } else {
4444  $this->SetXY($this->original_lMargin, $this->header_margin);
4445  }
4446  $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]);
4447  $this->Header();
4448  //restore position
4449  if ($this->rtl) {
4450  $this->SetXY($this->original_rMargin, $this->tMargin);
4451  } else {
4452  $this->SetXY($this->original_lMargin, $this->tMargin);
4453  }
4454  $this->_out('Q');
4455  $this->lasth = $lasth;
4456  $this->thead = $temp_thead;
4457  $this->theadMargins = $temp_theadMargins;
4458  $this->newline = false;
4459  $this->InHeader = false;
4460  }
4461 
4467  protected function setFooter() {
4468  if ($this->state != 2) {
4469  return;
4470  }
4471  $this->InFooter = true;
4472  // save current graphic settings
4473  $gvars = $this->getGraphicVars();
4474  // mark this point
4475  $this->footerpos[$this->page] = $this->pagelen[$this->page];
4476  $this->_out("\n");
4477  if ($this->print_footer) {
4478  $this->setGraphicVars($this->default_graphic_vars);
4479  $this->current_column = 0;
4480  $this->num_columns = 1;
4481  $temp_thead = $this->thead;
4482  $temp_theadMargins = $this->theadMargins;
4483  $lasth = $this->lasth;
4484  $this->_out('q');
4485  $this->rMargin = $this->original_rMargin;
4486  $this->lMargin = $this->original_lMargin;
4487  $this->SetCellPadding(0);
4488  //set current position
4489  $footer_y = $this->h - $this->footer_margin;
4490  if ($this->rtl) {
4491  $this->SetXY($this->original_rMargin, $footer_y);
4492  } else {
4493  $this->SetXY($this->original_lMargin, $footer_y);
4494  }
4495  $this->SetFont($this->footer_font[0], $this->footer_font[1], $this->footer_font[2]);
4496  $this->Footer();
4497  //restore position
4498  if ($this->rtl) {
4499  $this->SetXY($this->original_rMargin, $this->tMargin);
4500  } else {
4501  $this->SetXY($this->original_lMargin, $this->tMargin);
4502  }
4503  $this->_out('Q');
4504  $this->lasth = $lasth;
4505  $this->thead = $temp_thead;
4506  $this->theadMargins = $temp_theadMargins;
4507  }
4508  // restore graphic settings
4509  $this->setGraphicVars($gvars);
4510  $this->current_column = $gvars['current_column'];
4511  $this->num_columns = $gvars['num_columns'];
4512  // calculate footer length
4513  $this->footerlen[$this->page] = $this->pagelen[$this->page] - $this->footerpos[$this->page] + 1;
4514  $this->InFooter = false;
4515  }
4516 
4523  protected function inPageBody() {
4524  return (($this->InHeader === false) AND ($this->InFooter === false));
4525  }
4526 
4532  protected function setTableHeader() {
4533  if ($this->num_columns > 1) {
4534  // multi column mode
4535  return;
4536  }
4537  if (isset($this->theadMargins['top'])) {
4538  // restore the original top-margin
4539  $this->tMargin = $this->theadMargins['top'];
4540  $this->pagedim[$this->page]['tm'] = $this->tMargin;
4541  $this->y = $this->tMargin;
4542  }
4543  if (!$this->empty_string($this->thead) AND (!$this->inthead)) {
4544  // set margins
4545  $prev_lMargin = $this->lMargin;
4546  $prev_rMargin = $this->rMargin;
4547  $prev_cell_padding = $this->cell_padding;
4548  $this->lMargin = $this->theadMargins['lmargin'] + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$this->theadMargins['page']]['olm']);
4549  $this->rMargin = $this->theadMargins['rmargin'] + ($this->pagedim[$this->page]['orm'] - $this->pagedim[$this->theadMargins['page']]['orm']);
4550  $this->cell_padding = $this->theadMargins['cell_padding'];
4551  if ($this->rtl) {
4552  $this->x = $this->w - $this->rMargin;
4553  } else {
4554  $this->x = $this->lMargin;
4555  }
4556  // account for special "cell" mode
4557  if ($this->theadMargins['cell']) {
4558  if ($this->rtl) {
4559  $this->x -= $this->cell_padding['R'];
4560  } else {
4561  $this->x += $this->cell_padding['L'];
4562  }
4563  }
4564  // print table header
4565  $this->writeHTML($this->thead, false, false, false, false, '');
4566  // set new top margin to skip the table headers
4567  if (!isset($this->theadMargins['top'])) {
4568  $this->theadMargins['top'] = $this->tMargin;
4569  }
4570  // store end of header position
4571  if (!isset($this->columns[0]['th'])) {
4572  $this->columns[0]['th'] = array();
4573  }
4574  $this->columns[0]['th']['\''.$this->page.'\''] = $this->y;
4575  $this->tMargin = $this->y;
4576  $this->pagedim[$this->page]['tm'] = $this->tMargin;
4577  $this->lasth = 0;
4578  $this->lMargin = $prev_lMargin;
4579  $this->rMargin = $prev_rMargin;
4580  $this->cell_padding = $prev_cell_padding;
4581  }
4582  }
4583 
4591  public function PageNo() {
4592  return $this->page;
4593  }
4594 
4608  public function AddSpotColor($name, $c, $m, $y, $k) {
4609  if (!isset($this->spot_colors[$name])) {
4610  $i = (1 + count($this->spot_colors));
4611  $this->spot_colors[$name] = array('C' => $c, 'M' => $m, 'Y' => $y, 'K' => $k, 'name' => $name, 'i' => $i);
4612  }
4613  }
4614 
4622  public function getSpotColor($name) {
4623  if (isset($this->spot_colors[$name])) {
4624  return $this->spot_colors[$name];
4625  }
4626  $color = preg_replace('/[\s]*/', '', $name); // remove extra spaces
4627  $color = strtolower($color);
4628  if (isset($this->spotcolor[$color])) {
4629  $this->AddSpotColor($this->spotcolor[$color][4], $this->spotcolor[$color][0], $this->spotcolor[$color][1], $this->spotcolor[$color][2], $this->spotcolor[$color][3]);
4630  return $this->spot_colors[$this->spotcolor[$color][4]];
4631  }
4632  return false;
4633  }
4634 
4644  public function setSpotColor($type, $name, $tint=100) {
4645  $spotcolor = $this->getSpotColor($name);
4646  if ($spotcolor === false) {
4647  $this->Error('Undefined spot color: '.$name.', you must add it on the spotcolors.php file.');
4648  }
4649  $tint = (max(0, min(100, $tint)) / 100);
4650  $pdfcolor = sprintf('/CS%d ', $this->spot_colors[$name]['i']);
4651  switch ($type) {
4652  case 'draw': {
4653  $pdfcolor .= sprintf('CS %F SCN', $tint);
4654  $this->DrawColor = $pdfcolor;
4655  $this->strokecolor = $spotcolor;
4656  break;
4657  }
4658  case 'fill': {
4659  $pdfcolor .= sprintf('cs %F scn', $tint);
4660  $this->FillColor = $pdfcolor;
4661  $this->bgcolor = $spotcolor;
4662  break;
4663  }
4664  case 'text': {
4665  $pdfcolor .= sprintf('cs %F scn', $tint);
4666  $this->TextColor = $pdfcolor;
4667  $this->fgcolor = $spotcolor;
4668  break;
4669  }
4670  }
4671  $this->ColorFlag = ($this->FillColor != $this->TextColor);
4672  if ($this->state == 2) {
4673  $this->_out($pdfcolor);
4674  }
4675  if ($this->inxobj) {
4676  // we are inside an XObject template
4677  $this->xobjects[$this->xobjid]['spot_colors'][$name] = $this->spot_colors[$name];
4678  }
4679  return $pdfcolor;
4680  }
4681 
4690  public function SetDrawSpotColor($name, $tint=100) {
4691  $this->setSpotColor('draw', $name, $tint);
4692  }
4693 
4702  public function SetFillSpotColor($name, $tint=100) {
4703  $this->setSpotColor('fill', $name, $tint);
4704  }
4705 
4714  public function SetTextSpotColor($name, $tint=100) {
4715  $this->setSpotColor('text', $name, $tint);
4716  }
4717 
4729  public function setColorArray($type, $color, $ret=false) {
4730  if (is_array($color)) {
4731  $color = array_values($color);
4732  // component: grey, RGB red or CMYK cyan
4733  $c = isset($color[0]) ? $color[0] : -1;
4734  // component: RGB green or CMYK magenta
4735  $m = isset($color[1]) ? $color[1] : -1;
4736  // component: RGB blue or CMYK yellow
4737  $y = isset($color[2]) ? $color[2] : -1;
4738  // component: CMYK black
4739  $k = isset($color[3]) ? $color[3] : -1;
4740  // color name
4741  $name = isset($color[4]) ? $color[4] : '';
4742  if ($c >= 0) {
4743  return $this->setColor($type, $c, $m, $y, $k, $ret, $name);
4744  }
4745  }
4746  return '';
4747  }
4748 
4760  public function SetDrawColorArray($color, $ret=false) {
4761  return $this->setColorArray('draw', $color, $ret);
4762  }
4763 
4774  public function SetFillColorArray($color, $ret=false) {
4775  return $this->setColorArray('fill', $color, $ret);
4776  }
4777 
4787  public function SetTextColorArray($color, $ret=false) {
4788  return $this->setColorArray('text', $color, $ret);
4789  }
4790 
4804  public function setColor($type, $col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
4805  // set default values
4806  if (!is_numeric($col1)) {
4807  $col1 = 0;
4808  }
4809  if (!is_numeric($col2)) {
4810  $col2 = -1;
4811  }
4812  if (!is_numeric($col3)) {
4813  $col3 = -1;
4814  }
4815  if (!is_numeric($col4)) {
4816  $col4 = -1;
4817  }
4818  // set color by case
4819  $suffix = '';
4820  if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
4821  // Grey scale
4822  $col1 = max(0, min(255, $col1));
4823  $intcolor = array('G' => $col1);
4824  $pdfcolor = sprintf('%F ', ($col1 / 255));
4825  $suffix = 'g';
4826  } elseif ($col4 == -1) {
4827  // RGB
4828  $col1 = max(0, min(255, $col1));
4829  $col2 = max(0, min(255, $col2));
4830  $col3 = max(0, min(255, $col3));
4831  $intcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
4832  $pdfcolor = sprintf('%F %F %F ', ($col1 / 255), ($col2 / 255), ($col3 / 255));
4833  $suffix = 'rg';
4834  } else {
4835  $col1 = max(0, min(100, $col1));
4836  $col2 = max(0, min(100, $col2));
4837  $col3 = max(0, min(100, $col3));
4838  $col4 = max(0, min(100, $col4));
4839  if (empty($name)) {
4840  // CMYK
4841  $intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
4842  $pdfcolor = sprintf('%F %F %F %F ', ($col1 / 100), ($col2 / 100), ($col3 / 100), ($col4 / 100));
4843  $suffix = 'k';
4844  } else {
4845  // SPOT COLOR
4846  $intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4, 'name' => $name);
4847  $this->AddSpotColor($name, $col1, $col2, $col3, $col4);
4848  $pdfcolor = $this->setSpotColor($type, $name, 100);
4849  }
4850  }
4851  switch ($type) {
4852  case 'draw': {
4853  $pdfcolor .= strtoupper($suffix);
4854  $this->DrawColor = $pdfcolor;
4855  $this->strokecolor = $intcolor;
4856  break;
4857  }
4858  case 'fill': {
4859  $pdfcolor .= $suffix;
4860  $this->FillColor = $pdfcolor;
4861  $this->bgcolor = $intcolor;
4862  break;
4863  }
4864  case 'text': {
4865  $pdfcolor .= $suffix;
4866  $this->TextColor = $pdfcolor;
4867  $this->fgcolor = $intcolor;
4868  break;
4869  }
4870  }
4871  $this->ColorFlag = ($this->FillColor != $this->TextColor);
4872  if (($type != 'text') AND ($this->state == 2)) {
4873  if (!$ret) {
4874  $this->_out($pdfcolor);
4875  }
4876  return $pdfcolor;
4877  }
4878  return '';
4879  }
4880 
4888  protected function getColorStringFromArray($c) {
4889  $c = array_values($c);
4890  $color = '[';
4891  switch (count($c)) {
4892  case 4: {
4893  // CMYK
4894  $color .= sprintf('%F %F %F %F', (max(0, min(100, floatval($c[0]))) / 100), (max(0, min(100, floatval($c[1]))) / 100), (max(0, min(100, floatval($c[2]))) / 100), (max(0, min(100, floatval($c[3]))) / 100));
4895  break;
4896  }
4897  case 3: {
4898  // RGB
4899  $color .= sprintf('%F %F %F', (max(0, min(255, floatval($c[0]))) / 255), (max(0, min(255, floatval($c[1]))) / 255), (max(0, min(255, floatval($c[2]))) / 255));
4900  break;
4901  }
4902  case 1: {
4903  // grayscale
4904  $color .= sprintf('%F', (max(0, min(255, floatval($c[0]))) / 255));
4905  break;
4906  }
4907  }
4908  $color .= ']';
4909  return $color;
4910  }
4911 
4925  public function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
4926  return $this->setColor('draw', $col1, $col2, $col3, $col4, $ret, $name);
4927  }
4928 
4942  public function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
4943  return $this->setColor('fill', $col1, $col2, $col3, $col4, $ret, $name);
4944  }
4945 
4959  public function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
4960  return $this->setColor('text', $col1, $col2, $col3, $col4, $ret, $name);
4961  }
4962 
4975  public function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
4976  return $this->GetArrStringWidth($this->utf8Bidi($this->UTF8StringToArray($s), $s, $this->tmprtl), $fontname, $fontstyle, $fontsize, $getarray);
4977  }
4978 
4991  public function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
4992  // store current values
4993  if (!$this->empty_string($fontname)) {
4994  $prev_FontFamily = $this->FontFamily;
4995  $prev_FontStyle = $this->FontStyle;
4996  $prev_FontSizePt = $this->FontSizePt;
4997  $this->SetFont($fontname, $fontstyle, $fontsize, '', 'default', false);
4998  }
4999  // convert UTF-8 array to Latin1 if required
5000  $sa = $this->UTF8ArrToLatin1($sa);
5001  $w = 0; // total width
5002  $wa = array(); // array of characters widths
5003  foreach ($sa as $ck => $char) {
5004  // character width
5005  $cw = $this->GetCharWidth($char, isset($sa[($ck + 1)]));
5006  $wa[] = $cw;
5007  $w += $cw;
5008  }
5009  // restore previous values
5010  if (!$this->empty_string($fontname)) {
5011  $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt, '', 'default', false);
5012  }
5013  if ($getarray) {
5014  return $wa;
5015  }
5016  return $w;
5017  }
5018 
5028  public function GetCharWidth($char, $notlast=true) {
5029  // get raw width
5030  $chw = $this->getRawCharWidth($char);
5031  if (($this->font_spacing < 0) OR (($this->font_spacing > 0) AND $notlast)) {
5032  // increase/decrease font spacing
5033  $chw += $this->font_spacing;
5034  }
5035  if ($this->font_stretching != 100) {
5036  // fixed stretching mode
5037  $chw *= ($this->font_stretching / 100);
5038  }
5039  return $chw;
5040  }
5041 
5050  public function getRawCharWidth($char) {
5051  if ($char == 173) {
5052  // SHY character will not be printed
5053  return (0);
5054  }
5055  if (isset($this->CurrentFont['cw'][$char])) {
5056  $w = $this->CurrentFont['cw'][$char];
5057  } elseif (isset($this->CurrentFont['dw'])) {
5058  // default width
5059  $w = $this->CurrentFont['dw'];
5060  } elseif (isset($this->CurrentFont['cw'][32])) {
5061  // default width
5062  $w = $this->CurrentFont['cw'][32];
5063  } else {
5064  $w = 600;
5065  }
5066  return $this->getAbsFontMeasure($w);
5067  }
5068 
5076  public function GetNumChars($s) {
5077  if ($this->isUnicodeFont()) {
5078  return count($this->UTF8StringToArray($s));
5079  }
5080  return strlen($s);
5081  }
5082 
5088  protected function getFontsList() {
5089  $fontsdir = opendir($this->_getfontpath());
5090  while (($file = readdir($fontsdir)) !== false) {
5091  if (substr($file, -4) == '.php') {
5092  array_push($this->fontlist, strtolower(basename($file, '.php')));
5093  }
5094  }
5095  closedir($fontsdir);
5096  }
5097 
5111  public function AddFont($family, $style='', $fontfile='', $subset='default') {
5112  if ($subset === 'default') {
5113  $subset = $this->font_subsetting;
5114  }
5115  if ($this->pdfa_mode) {
5116  $subset = false;
5117  }
5118  if ($this->empty_string($family)) {
5119  if (!$this->empty_string($this->FontFamily)) {
5120  $family = $this->FontFamily;
5121  } else {
5122  $this->Error('Empty font family');
5123  }
5124  }
5125  // move embedded styles on $style
5126  if (substr($family, -1) == 'I') {
5127  $style .= 'I';
5128  $family = substr($family, 0, -1);
5129  }
5130  if (substr($family, -1) == 'B') {
5131  $style .= 'B';
5132  $family = substr($family, 0, -1);
5133  }
5134  // normalize family name
5135  $family = strtolower($family);
5136  if ((!$this->isunicode) AND ($family == 'arial')) {
5137  $family = 'helvetica';
5138  }
5139  if (($family == 'symbol') OR ($family == 'zapfdingbats')) {
5140  $style = '';
5141  }
5142  if ($this->pdfa_mode AND (isset($this->CoreFonts[$family]))) {
5143  // all fonts must be embedded
5144  $family = 'pdfa'.$family;
5145  }
5146  $tempstyle = strtoupper($style);
5147  $style = '';
5148  // underline
5149  if (strpos($tempstyle, 'U') !== false) {
5150  $this->underline = true;
5151  } else {
5152  $this->underline = false;
5153  }
5154  // line-through (deleted)
5155  if (strpos($tempstyle, 'D') !== false) {
5156  $this->linethrough = true;
5157  } else {
5158  $this->linethrough = false;
5159  }
5160  // overline
5161  if (strpos($tempstyle, 'O') !== false) {
5162  $this->overline = true;
5163  } else {
5164  $this->overline = false;
5165  }
5166  // bold
5167  if (strpos($tempstyle, 'B') !== false) {
5168  $style .= 'B';
5169  }
5170  // oblique
5171  if (strpos($tempstyle, 'I') !== false) {
5172  $style .= 'I';
5173  }
5174  $bistyle = $style;
5175  $fontkey = $family.$style;
5176  $font_style = $style.($this->underline ? 'U' : '').($this->linethrough ? 'D' : '').($this->overline ? 'O' : '');
5177  $fontdata = array('fontkey' => $fontkey, 'family' => $family, 'style' => $font_style);
5178  // check if the font has been already added
5179  $fb = $this->getFontBuffer($fontkey);
5180  if ($fb !== false) {
5181  if ($this->inxobj) {
5182  // we are inside an XObject template
5183  $this->xobjects[$this->xobjid]['fonts'][$fontkey] = $fb['i'];
5184  }
5185  return $fontdata;
5186  }
5187  if (isset($type)) {
5188  unset($type);
5189  }
5190  if (isset($cw)) {
5191  unset($cw);
5192  }
5193  // get specified font directory (if any)
5194  $fontdir = false;
5195  if (!$this->empty_string($fontfile)) {
5196  $fontdir = dirname($fontfile);
5197  if ($this->empty_string($fontdir) OR ($fontdir == '.')) {
5198  $fontdir = '';
5199  } else {
5200  $fontdir .= '/';
5201  }
5202  }
5203  $missing_style = false; // true when the font style variation is missing
5204  // search and include font file
5205  if ($this->empty_string($fontfile) OR (!file_exists($fontfile))) {
5206  // build a standard filenames for specified font
5207  $tmp_fontfile = str_replace(' ', '', $family).strtolower($style).'.php';
5208  // search files on various directories
5209  if (($fontdir !== false) AND file_exists($fontdir.$tmp_fontfile)) {
5210  $fontfile = $fontdir.$tmp_fontfile;
5211  } elseif (file_exists($this->_getfontpath().$tmp_fontfile)) {
5212  $fontfile = $this->_getfontpath().$tmp_fontfile;
5213  } elseif (file_exists($tmp_fontfile)) {
5214  $fontfile = $tmp_fontfile;
5215  } elseif (!$this->empty_string($style)) {
5216  $missing_style = true;
5217  // try to remove the style part
5218  $tmp_fontfile = str_replace(' ', '', $family).'.php';
5219  if (($fontdir !== false) AND file_exists($fontdir.$tmp_fontfile)) {
5220  $fontfile = $fontdir.$tmp_fontfile;
5221  } elseif (file_exists($this->_getfontpath().$tmp_fontfile)) {
5222  $fontfile = $this->_getfontpath().$tmp_fontfile;
5223  } else {
5224  $fontfile = $tmp_fontfile;
5225  }
5226  }
5227  }
5228  // include font file
5229  if (file_exists($fontfile)) {
5230  include($fontfile);
5231  } else {
5232  $this->Error('Could not include font definition file: '.$family.'');
5233  }
5234  // check font parameters
5235  if ((!isset($type)) OR (!isset($cw))) {
5236  $this->Error('The font definition file has a bad format: '.$fontfile.'');
5237  }
5238  // SET default parameters
5239  if (!isset($file) OR $this->empty_string($file)) {
5240  $file = '';
5241  }
5242  if (!isset($enc) OR $this->empty_string($enc)) {
5243  $enc = '';
5244  }
5245  if (!isset($cidinfo) OR $this->empty_string($cidinfo)) {
5246  $cidinfo = array('Registry'=>'Adobe', 'Ordering'=>'Identity', 'Supplement'=>0);
5247  $cidinfo['uni2cid'] = array();
5248  }
5249  if (!isset($ctg) OR $this->empty_string($ctg)) {
5250  $ctg = '';
5251  }
5252  if (!isset($desc) OR $this->empty_string($desc)) {
5253  $desc = array();
5254  }
5255  if (!isset($up) OR $this->empty_string($up)) {
5256  $up = -100;
5257  }
5258  if (!isset($ut) OR $this->empty_string($ut)) {
5259  $ut = 50;
5260  }
5261  if (!isset($cw) OR $this->empty_string($cw)) {
5262  $cw = array();
5263  }
5264  if (!isset($dw) OR $this->empty_string($dw)) {
5265  // set default width
5266  if (isset($desc['MissingWidth']) AND ($desc['MissingWidth'] > 0)) {
5267  $dw = $desc['MissingWidth'];
5268  } elseif (isset($cw[32])) {
5269  $dw = $cw[32];
5270  } else {
5271  $dw = 600;
5272  }
5273  }
5274  ++$this->numfonts;
5275  if ($type == 'core') {
5276  $name = $this->CoreFonts[$fontkey];
5277  $subset = false;
5278  } elseif (($type == 'TrueType') OR ($type == 'Type1')) {
5279  $subset = false;
5280  } elseif ($type == 'TrueTypeUnicode') {
5281  $enc = 'Identity-H';
5282  } elseif ($type == 'cidfont0') {
5283  if ($this->pdfa_mode) {
5284  $this->Error('All fonts must be embedded in PDF/A mode!');
5285  }
5286  } else {
5287  $this->Error('Unknow font type: '.$type.'');
5288  }
5289  // set name if unset
5290  if (!isset($name) OR empty($name)) {
5291  $name = $fontkey;
5292  }
5293  // create artificial font style variations if missing (only works with non-embedded fonts)
5294  if (($type != 'core') AND $missing_style) {
5295  // style variations
5296  $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic');
5297  $name .= $styles[$bistyle];
5298  // artificial bold
5299  if (strpos($bistyle, 'B') !== false) {
5300  if (isset($desc['StemV'])) {
5301  // from normal to bold
5302  $desc['StemV'] = round($desc['StemV'] * 1.75);
5303  } else {
5304  // bold
5305  $desc['StemV'] = 123;
5306  }
5307  }
5308  // artificial italic
5309  if (strpos($bistyle, 'I') !== false) {
5310  if (isset($desc['ItalicAngle'])) {
5311  $desc['ItalicAngle'] -= 11;
5312  } else {
5313  $desc['ItalicAngle'] = -11;
5314  }
5315  if (isset($desc['Flags'])) {
5316  $desc['Flags'] |= 64; //bit 7
5317  } else {
5318  $desc['Flags'] = 64;
5319  }
5320  }
5321  }
5322  // check if the array of characters bounding boxes is defined
5323  if (!isset($cbbox)) {
5324  $cbbox = array();
5325  }
5326  // initialize subsetchars
5327  $subsetchars = array_fill(0, 255, true);
5328  $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));
5329  if ($this->inxobj) {
5330  // we are inside an XObject template
5331  $this->xobjects[$this->xobjid]['fonts'][$fontkey] = $this->numfonts;
5332  }
5333  if (isset($diff) AND (!empty($diff))) {
5334  //Search existing encodings
5335  $d = 0;
5336  $nb = count($this->diffs);
5337  for ($i=1; $i <= $nb; ++$i) {
5338  if ($this->diffs[$i] == $diff) {
5339  $d = $i;
5340  break;
5341  }
5342  }
5343  if ($d == 0) {
5344  $d = $nb + 1;
5345  $this->diffs[$d] = $diff;
5346  }
5347  $this->setFontSubBuffer($fontkey, 'diff', $d);
5348  }
5349  if (!$this->empty_string($file)) {
5350  if (!isset($this->FontFiles[$file])) {
5351  if ((strcasecmp($type,'TrueType') == 0) OR (strcasecmp($type, 'TrueTypeUnicode') == 0)) {
5352  $this->FontFiles[$file] = array('length1' => $originalsize, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey));
5353  } elseif ($type != 'core') {
5354  $this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey));
5355  }
5356  } else {
5357  // update fontkeys that are sharing this font file
5358  $this->FontFiles[$file]['subset'] = ($this->FontFiles[$file]['subset'] AND $subset);
5359  if (!in_array($fontkey, $this->FontFiles[$file]['fontkeys'])) {
5360  $this->FontFiles[$file]['fontkeys'][] = $fontkey;
5361  }
5362  }
5363  }
5364  return $fontdata;
5365  }
5366 
5384  public function SetFont($family, $style='', $size=null, $fontfile='', $subset='default', $out=true) {
5385  //Select a font; size given in points
5386  if ($size === null) {
5387  $size = $this->FontSizePt;
5388  }
5389  if ($size < 0) {
5390  $size = 0;
5391  }
5392  // try to add font (if not already added)
5393  $fontdata = $this->AddFont($family, $style, $fontfile, $subset);
5394  $this->FontFamily = $fontdata['family'];
5395  $this->FontStyle = $fontdata['style'];
5396  $this->CurrentFont = $this->getFontBuffer($fontdata['fontkey']);
5397  $this->SetFontSize($size, $out);
5398  }
5399 
5408  public function SetFontSize($size, $out=true) {
5409  // font size in points
5410  $this->FontSizePt = $size;
5411  // font size in user units
5412  $this->FontSize = $size / $this->k;
5413  // calculate some font metrics
5414  if (isset($this->CurrentFont['desc']['FontBBox'])) {
5415  $bbox = explode(' ', substr($this->CurrentFont['desc']['FontBBox'], 1, -1));
5416  $font_height = ((intval($bbox[3]) - intval($bbox[1])) * $size / 1000);
5417  } else {
5418  $font_height = $size * 1.219;
5419  }
5420  if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) {
5421  $font_ascent = ($this->CurrentFont['desc']['Ascent'] * $size / 1000);
5422  }
5423  if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] <= 0)) {
5424  $font_descent = (- $this->CurrentFont['desc']['Descent'] * $size / 1000);
5425  }
5426  if (!isset($font_ascent) AND !isset($font_descent)) {
5427  // core font
5428  $font_ascent = 0.76 * $font_height;
5429  $font_descent = $font_height - $font_ascent;
5430  } elseif (!isset($font_descent)) {
5431  $font_descent = $font_height - $font_ascent;
5432  } elseif (!isset($font_ascent)) {
5433  $font_ascent = $font_height - $font_descent;
5434  }
5435  $this->FontAscent = ($font_ascent / $this->k);
5436  $this->FontDescent = ($font_descent / $this->k);
5437  if ($out AND ($this->page > 0) AND (isset($this->CurrentFont['i'])) AND ($this->state == 2)) {
5438  $this->_out(sprintf('BT /F%d %F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
5439  }
5440  }
5441 
5448  public function getFontBBox() {
5449  $fbbox = array();
5450  if (isset($this->CurrentFont['desc']['FontBBox'])) {
5451  $tmpbbox = explode(' ', substr($this->CurrentFont['desc']['FontBBox'], 1, -1));
5452  $fbbox = array_map(array($this,'getAbsFontMeasure'), $tmpbbox);
5453  } else {
5454  // Find max width
5455  if (isset($this->CurrentFont['desc']['MaxWidth'])) {
5456  $maxw = $this->getAbsFontMeasure(intval($this->CurrentFont['desc']['MaxWidth']));
5457  } else {
5458  $maxw = 0;
5459  if (isset($this->CurrentFont['desc']['MissingWidth'])) {
5460  $maxw = max($maxw, $this->CurrentFont['desc']['MissingWidth']);
5461  }
5462  if (isset($this->CurrentFont['desc']['AvgWidth'])) {
5463  $maxw = max($maxw, $this->CurrentFont['desc']['AvgWidth']);
5464  }
5465  if (isset($this->CurrentFont['dw'])) {
5466  $maxw = max($maxw, $this->CurrentFont['dw']);
5467  }
5468  foreach ($this->CurrentFont['cw'] as $char => $w) {
5469  $maxw = max($maxw, $w);
5470  }
5471  if ($maxw == 0) {
5472  $maxw = 600;
5473  }
5474  $maxw = $this->getAbsFontMeasure($maxw);
5475  }
5476  $fbbox = array(0, -$this->FontDescent, $maxw, $this->FontAscent);
5477  }
5478  return $fbbox;
5479  }
5480 
5487  public function getAbsFontMeasure($s) {
5488  return ($s * $this->FontSize / 1000);
5489  }
5490 
5497  public function getCharBBox($char) {
5498  if (isset($this->CurrentFont['cbbox'][$char])) {
5499  return array_map(array($this,'getAbsFontMeasure'), $this->CurrentFont['cbbox'][intval($char)]);
5500  }
5501  return false;
5502  }
5503 
5514  public function getFontDescent($font, $style='', $size=0) {
5515  $fontdata = $this->AddFont($font, $style);
5516  $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
5517  if (isset($fontinfo['desc']['Descent']) AND ($fontinfo['desc']['Descent'] <= 0)) {
5518  $descent = (- $fontinfo['desc']['Descent'] * $size / 1000);
5519  } else {
5520  $descent = (1.219 * 0.24 * $size);
5521  }
5522  return ($descent / $this->k);
5523  }
5524 
5535  public function getFontAscent($font, $style='', $size=0) {
5536  $fontdata = $this->AddFont($font, $style);
5537  $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
5538  if (isset($fontinfo['desc']['Ascent']) AND ($fontinfo['desc']['Ascent'] > 0)) {
5539  $ascent = ($fontinfo['desc']['Ascent'] * $size / 1000);
5540  } else {
5541  $ascent = 1.219 * 0.76 * $size;
5542  }
5543  return ($ascent / $this->k);
5544  }
5545 
5555  public function isCharDefined($char, $font='', $style='') {
5556  if (is_string($char)) {
5557  // get character code
5558  $char = $this->UTF8StringToArray($char);
5559  $char = $char[0];
5560  }
5561  if ($this->empty_string($font)) {
5562  if ($this->empty_string($style)) {
5563  return (isset($this->CurrentFont['cw'][intval($char)]));
5564  }
5565  $font = $this->FontFamily;
5566  }
5567  $fontdata = $this->AddFont($font, $style);
5568  $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
5569  return (isset($fontinfo['cw'][intval($char)]));
5570  }
5571 
5582  public function replaceMissingChars($text, $font='', $style='', $subs=array()) {
5583  if (empty($subs)) {
5584  return $text;
5585  }
5586  if ($this->empty_string($font)) {
5587  $font = $this->FontFamily;
5588  }
5589  $fontdata = $this->AddFont($font, $style);
5590  $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
5591  $uniarr = $this->UTF8StringToArray($text);
5592  foreach ($uniarr as $k => $chr) {
5593  if (!isset($fontinfo['cw'][$chr])) {
5594  // this character is missing on the selected font
5595  if (isset($subs[$chr])) {
5596  // we have available substitutions
5597  if (is_array($subs[$chr])) {
5598  foreach($subs[$chr] as $s) {
5599  if (isset($fontinfo['cw'][$s])) {
5600  $uniarr[$k] = $s;
5601  break;
5602  }
5603  }
5604  } elseif (isset($fontinfo['cw'][$subs[$chr]])) {
5605  $uniarr[$k] = $subs[$chr];
5606  }
5607  }
5608  }
5609  }
5610  return $this->UniArrSubString($this->UTF8ArrayToUniArray($uniarr));
5611  }
5612 
5619  public function SetDefaultMonospacedFont($font) {
5620  $this->default_monospaced_font = $font;
5621  }
5622 
5630  public function AddLink() {
5631  //Create a new internal link
5632  $n = count($this->links) + 1;
5633  $this->links[$n] = array(0, 0);
5634  return $n;
5635  }
5636 
5646  public function SetLink($link, $y=0, $page=-1) {
5647  if ($y == -1) {
5648  $y = $this->y;
5649  }
5650  if ($page == -1) {
5651  $page = $this->page;
5652  }
5653  $this->links[$link] = array($page, $y);
5654  }
5655 
5669  public function Link($x, $y, $w, $h, $link, $spaces=0) {
5670  $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'), $spaces);
5671  }
5672 
5686  public function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text'), $spaces=0) {
5687  if ($this->inxobj) {
5688  // store parameters for later use on template
5689  $this->xobjects[$this->xobjid]['annotations'][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'text' => $text, 'opt' => $opt, 'spaces' => $spaces);
5690  return;
5691  }
5692  if ($x === '') {
5693  $x = $this->x;
5694  }
5695  if ($y === '') {
5696  $y = $this->y;
5697  }
5698  // check page for no-write regions and adapt page margins if necessary
5699  list($x, $y) = $this->checkPageRegions($h, $x, $y);
5700  // recalculate coordinates to account for graphic transformations
5701  if (isset($this->transfmatrix) AND !empty($this->transfmatrix)) {
5702  for ($i=$this->transfmatrix_key; $i > 0; --$i) {
5703  $maxid = count($this->transfmatrix[$i]) - 1;
5704  for ($j=$maxid; $j >= 0; --$j) {
5705  $ctm = $this->transfmatrix[$i][$j];
5706  if (isset($ctm['a'])) {
5707  $x = $x * $this->k;
5708  $y = ($this->h - $y) * $this->k;
5709  $w = $w * $this->k;
5710  $h = $h * $this->k;
5711  // top left
5712  $xt = $x;
5713  $yt = $y;
5714  $x1 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
5715  $y1 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
5716  // top right
5717  $xt = $x + $w;
5718  $yt = $y;
5719  $x2 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
5720  $y2 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
5721  // bottom left
5722  $xt = $x;
5723  $yt = $y - $h;
5724  $x3 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
5725  $y3 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
5726  // bottom right
5727  $xt = $x + $w;
5728  $yt = $y - $h;
5729  $x4 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
5730  $y4 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
5731  // new coordinates (rectangle area)
5732  $x = min($x1, $x2, $x3, $x4);
5733  $y = max($y1, $y2, $y3, $y4);
5734  $w = (max($x1, $x2, $x3, $x4) - $x) / $this->k;
5735  $h = ($y - min($y1, $y2, $y3, $y4)) / $this->k;
5736  $x = $x / $this->k;
5737  $y = $this->h - ($y / $this->k);
5738  }
5739  }
5740  }
5741  }
5742  if ($this->page <= 0) {
5743  $page = 1;
5744  } else {
5745  $page = $this->page;
5746  }
5747  if (!isset($this->PageAnnots[$page])) {
5748  $this->PageAnnots[$page] = array();
5749  }
5750  ++$this->n;
5751  $this->PageAnnots[$page][] = array('n' => $this->n, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces);
5752  if (!$this->pdfa_mode) {
5753  if ((($opt['Subtype'] == 'FileAttachment') OR ($opt['Subtype'] == 'Sound')) AND (!$this->empty_string($opt['FS'])) AND file_exists($opt['FS']) AND (!isset($this->embeddedfiles[basename($opt['FS'])]))) {
5754  ++$this->n;
5755  $this->embeddedfiles[basename($opt['FS'])] = array('n' => $this->n, 'file' => $opt['FS']);
5756  }
5757  }
5758  // Add widgets annotation's icons
5759  if (isset($opt['mk']['i']) AND file_exists($opt['mk']['i'])) {
5760  $this->Image($opt['mk']['i'], '', '', 10, 10, '', '', '', false, 300, '', false, false, 0, false, true);
5761  }
5762  if (isset($opt['mk']['ri']) AND file_exists($opt['mk']['ri'])) {
5763  $this->Image($opt['mk']['ri'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
5764  }
5765  if (isset($opt['mk']['ix']) AND file_exists($opt['mk']['ix'])) {
5766  $this->Image($opt['mk']['ix'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
5767  }
5768  }
5769 
5776  protected function _putEmbeddedFiles() {
5777  if ($this->pdfa_mode) {
5778  // embedded files are not allowed in PDF/A mode
5779  return;
5780  }
5781  reset($this->embeddedfiles);
5782  foreach ($this->embeddedfiles as $filename => $filedata) {
5783  $data = file_get_contents($filedata['file']);
5784  $filter = '';
5785  if ($this->compress) {
5786  $data = gzcompress($data);
5787  $filter = ' /Filter /FlateDecode';
5788  }
5789  $stream = $this->_getrawstream($data, $filedata['n']);
5790  $out = $this->_getobj($filedata['n'])."\n";
5791  $out .= '<< /Type /EmbeddedFile'.$filter.' /Length '.strlen($stream).' >>';
5792  $out .= ' stream'."\n".$stream."\n".'endstream';
5793  $out .= "\n".'endobj';
5794  $this->_out($out);
5795  }
5796  }
5797 
5821  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) {
5824  $this->setTextRenderingMode($fstroke, $ffill, $fclip);
5825  $this->SetXY($x, $y, $rtloff);
5826  $this->Cell(0, 0, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height, $calign, $valign);
5827  // restore previous rendering mode
5828  $this->textrendermode = $textrendermode;
5829  $this->textstrokewidth = $textstrokewidth;
5830  }
5831 
5841  public function AcceptPageBreak() {
5842  if ($this->num_columns > 1) {
5843  // multi column mode
5844  if ($this->current_column < ($this->num_columns - 1)) {
5845  // go to next column
5846  $this->selectColumn($this->current_column + 1);
5847  } elseif ($this->AutoPageBreak) {
5848  // add a new page
5849  $this->AddPage();
5850  // set first column
5851  $this->selectColumn(0);
5852  }
5853  // avoid page breaking from checkPageBreak()
5854  return false;
5855  }
5856  return $this->AutoPageBreak;
5857  }
5858 
5868  protected function checkPageBreak($h=0, $y='', $addpage=true) {
5869  if ($this->empty_string($y)) {
5870  $y = $this->y;
5871  }
5872  $current_page = $this->page;
5873  if ((($y + $h) > $this->PageBreakTrigger) AND ($this->inPageBody()) AND ($this->AcceptPageBreak())) {
5874  if ($addpage) {
5875  //Automatic page break
5876  $x = $this->x;
5877  $this->AddPage($this->CurOrientation);
5878  $this->y = $this->tMargin;
5879  $oldpage = $this->page - 1;
5880  if ($this->rtl) {
5881  if ($this->pagedim[$this->page]['orm'] != $this->pagedim[$oldpage]['orm']) {
5882  $this->x = $x - ($this->pagedim[$this->page]['orm'] - $this->pagedim[$oldpage]['orm']);
5883  } else {
5884  $this->x = $x;
5885  }
5886  } else {
5887  if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
5888  $this->x = $x + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$oldpage]['olm']);
5889  } else {
5890  $this->x = $x;
5891  }
5892  }
5893  }
5894  return true;
5895  }
5896  if ($current_page != $this->page) {
5897  // account for columns mode
5898  return true;
5899  }
5900  return false;
5901  }
5902 
5919  public function removeSHY($txt='') {
5920  $txt = preg_replace('/([\\xc2]{1}[\\xad]{1})/', '', $txt);
5921  if (!$this->isunicode) {
5922  $txt = preg_replace('/([\\xad]{1})/', '', $txt);
5923  }
5924  return $txt;
5925  }
5926 
5946  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') {
5947  $prev_cell_margin = $this->cell_margin;
5948  $prev_cell_padding = $this->cell_padding;
5949  $this->adjustCellPadding($border);
5950  if (!$ignore_min_height) {
5951  $min_cell_height = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
5952  if ($h < $min_cell_height) {
5953  $h = $min_cell_height;
5954  }
5955  }
5956  $this->checkPageBreak($h + $this->cell_margin['T'] + $this->cell_margin['B']);
5957  // apply text shadow if enabled
5958  if ($this->txtshadow['enabled']) {
5959  // save data
5960  $x = $this->x;
5961  $y = $this->y;
5962  $bc = $this->bgcolor;
5963  $fc = $this->fgcolor;
5964  $sc = $this->strokecolor;
5965  $alpha = $this->alpha;
5966  // print shadow
5967  $this->x += $this->txtshadow['depth_w'];
5968  $this->y += $this->txtshadow['depth_h'];
5969  $this->SetFillColorArray($this->txtshadow['color']);
5970  $this->SetTextColorArray($this->txtshadow['color']);
5971  $this->SetDrawColorArray($this->txtshadow['color']);
5972  if ($this->txtshadow['opacity'] != $alpha['CA']) {
5973  $this->setAlpha($this->txtshadow['opacity'], $this->txtshadow['blend_mode']);
5974  }
5975  if ($this->state == 2) {
5976  $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign));
5977  }
5978  //restore data
5979  $this->x = $x;
5980  $this->y = $y;
5981  $this->SetFillColorArray($bc);
5982  $this->SetTextColorArray($fc);
5983  $this->SetDrawColorArray($sc);
5984  if ($this->txtshadow['opacity'] != $alpha['CA']) {
5985  $this->setAlpha($alpha['CA'], $alpha['BM'], $alpha['ca'], $alpha['AIS']);
5986  }
5987  }
5988  if ($this->state == 2) {
5989  $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign));
5990  }
5991  $this->cell_padding = $prev_cell_padding;
5992  $this->cell_margin = $prev_cell_margin;
5993  }
5994 
6015  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') {
6016  // replace 'NO-BREAK SPACE' (U+00A0) character with a simple space
6017  $txt = str_replace($this->unichr(160), ' ', $txt);
6018  $prev_cell_margin = $this->cell_margin;
6019  $prev_cell_padding = $this->cell_padding;
6020  $txt = $this->removeSHY($txt);
6021  $rs = ''; //string to be returned
6022  $this->adjustCellPadding($border);
6023  if (!$ignore_min_height) {
6024  $min_cell_height = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
6025  if ($h < $min_cell_height) {
6026  $h = $min_cell_height;
6027  }
6028  }
6029  $k = $this->k;
6030  // check page for no-write regions and adapt page margins if necessary
6031  list($this->x, $this->y) = $this->checkPageRegions($h, $this->x, $this->y);
6032  if ($this->rtl) {
6033  $x = $this->x - $this->cell_margin['R'];
6034  } else {
6035  $x = $this->x + $this->cell_margin['L'];
6036  }
6037  $y = $this->y + $this->cell_margin['T'];
6038  $prev_font_stretching = $this->font_stretching;
6039  $prev_font_spacing = $this->font_spacing;
6040  // cell vertical alignment
6041  switch ($calign) {
6042  case 'A': {
6043  // font top
6044  switch ($valign) {
6045  case 'T': {
6046  // top
6047  $y -= $this->cell_padding['T'];
6048  break;
6049  }
6050  case 'B': {
6051  // bottom
6052  $y -= ($h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent);
6053  break;
6054  }
6055  default:
6056  case 'C':
6057  case 'M': {
6058  // center
6059  $y -= (($h - $this->FontAscent - $this->FontDescent) / 2);
6060  break;
6061  }
6062  }
6063  break;
6064  }
6065  case 'L': {
6066  // font baseline
6067  switch ($valign) {
6068  case 'T': {
6069  // top
6070  $y -= ($this->cell_padding['T'] + $this->FontAscent);
6071  break;
6072  }
6073  case 'B': {
6074  // bottom
6075  $y -= ($h - $this->cell_padding['B'] - $this->FontDescent);
6076  break;
6077  }
6078  default:
6079  case 'C':
6080  case 'M': {
6081  // center
6082  $y -= (($h + $this->FontAscent - $this->FontDescent) / 2);
6083  break;
6084  }
6085  }
6086  break;
6087  }
6088  case 'D': {
6089  // font bottom
6090  switch ($valign) {
6091  case 'T': {
6092  // top
6093  $y -= ($this->cell_padding['T'] + $this->FontAscent + $this->FontDescent);
6094  break;
6095  }
6096  case 'B': {
6097  // bottom
6098  $y -= ($h - $this->cell_padding['B']);
6099  break;
6100  }
6101  default:
6102  case 'C':
6103  case 'M': {
6104  // center
6105  $y -= (($h + $this->FontAscent + $this->FontDescent) / 2);
6106  break;
6107  }
6108  }
6109  break;
6110  }
6111  case 'B': {
6112  // cell bottom
6113  $y -= $h;
6114  break;
6115  }
6116  case 'C':
6117  case 'M': {
6118  // cell center
6119  $y -= ($h / 2);
6120  break;
6121  }
6122  default:
6123  case 'T': {
6124  // cell top
6125  break;
6126  }
6127  }
6128  // text vertical alignment
6129  switch ($valign) {
6130  case 'T': {
6131  // top
6132  $yt = $y + $this->cell_padding['T'];
6133  break;
6134  }
6135  case 'B': {
6136  // bottom
6137  $yt = $y + $h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent;
6138  break;
6139  }
6140  default:
6141  case 'C':
6142  case 'M': {
6143  // center
6144  $yt = $y + (($h - $this->FontAscent - $this->FontDescent) / 2);
6145  break;
6146  }
6147  }
6148  $basefonty = $yt + $this->FontAscent;
6149  if ($this->empty_string($w) OR ($w <= 0)) {
6150  if ($this->rtl) {
6151  $w = $x - $this->lMargin;
6152  } else {
6153  $w = $this->w - $this->rMargin - $x;
6154  }
6155  }
6156  $s = '';
6157  // fill and borders
6158  if (is_string($border) AND (strlen($border) == 4)) {
6159  // full border
6160  $border = 1;
6161  }
6162  if ($fill OR ($border == 1)) {
6163  if ($fill) {
6164  $op = ($border == 1) ? 'B' : 'f';
6165  } else {
6166  $op = 'S';
6167  }
6168  if ($this->rtl) {
6169  $xk = (($x - $w) * $k);
6170  } else {
6171  $xk = ($x * $k);
6172  }
6173  $s .= sprintf('%F %F %F %F re %s ', $xk, (($this->h - $y) * $k), ($w * $k), (-$h * $k), $op);
6174  }
6175  // draw borders
6176  $s .= $this->getCellBorder($x, $y, $w, $h, $border);
6177  if ($txt != '') {
6178  $txt2 = $txt;
6179  if ($this->isunicode) {
6180  if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
6181  $txt2 = $this->UTF8ToLatin1($txt2);
6182  } else {
6183  $unicode = $this->UTF8StringToArray($txt); // array of UTF-8 unicode values
6184  $unicode = $this->utf8Bidi($unicode, '', $this->tmprtl);
6185  // replace thai chars (if any)
6186  if (defined('K_THAI_TOPCHARS') AND (K_THAI_TOPCHARS == true)) {
6187  // number of chars
6188  $numchars = count($unicode);
6189  // po pla, for far, for fan
6190  $longtail = array(0x0e1b, 0x0e1d, 0x0e1f);
6191  // do chada, to patak
6192  $lowtail = array(0x0e0e, 0x0e0f);
6193  // mai hun arkad, sara i, sara ii, sara ue, sara uee
6194  $upvowel = array(0x0e31, 0x0e34, 0x0e35, 0x0e36, 0x0e37);
6195  // mai ek, mai tho, mai tri, mai chattawa, karan
6196  $tonemark = array(0x0e48, 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c);
6197  // sara u, sara uu, pinthu
6198  $lowvowel = array(0x0e38, 0x0e39, 0x0e3a);
6199  $output = array();
6200  for ($i = 0; $i < $numchars; $i++) {
6201  if (($unicode[$i] >= 0x0e00) && ($unicode[$i] <= 0x0e5b)) {
6202  $ch0 = $unicode[$i];
6203  $ch1 = ($i > 0) ? $unicode[($i - 1)] : 0;
6204  $ch2 = ($i > 1) ? $unicode[($i - 2)] : 0;
6205  $chn = ($i < ($numchars - 1)) ? $unicode[($i + 1)] : 0;
6206  if (in_array($ch0, $tonemark)) {
6207  if ($chn == 0x0e33) {
6208  // sara um
6209  if (in_array($ch1, $longtail)) {
6210  // tonemark at upper left
6211  $output[] = $this->replaceChar($ch0, (0xf713 + $ch0 - 0x0e48));
6212  } else {
6213  // tonemark at upper right (normal position)
6214  $output[] = $ch0;
6215  }
6216  } elseif (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $lowvowel))) {
6217  // tonemark at lower left
6218  $output[] = $this->replaceChar($ch0, (0xf705 + $ch0 - 0x0e48));
6219  } elseif (in_array($ch1, $upvowel)) {
6220  if (in_array($ch2, $longtail)) {
6221  // tonemark at upper left
6222  $output[] = $this->replaceChar($ch0, (0xf713 + $ch0 - 0x0e48));
6223  } else {
6224  // tonemark at upper right (normal position)
6225  $output[] = $ch0;
6226  }
6227  } else {
6228  // tonemark at lower right
6229  $output[] = $this->replaceChar($ch0, (0xf70a + $ch0 - 0x0e48));
6230  }
6231  } elseif (($ch0 == 0x0e33) AND (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $tonemark)))) {
6232  // add lower left nikhahit and sara aa
6233  if ($this->isCharDefined(0xf711) AND $this->isCharDefined(0x0e32)) {
6234  $output[] = 0xf711;
6235  $this->CurrentFont['subsetchars'][0xf711] = true;
6236  $output[] = 0x0e32;
6237  $this->CurrentFont['subsetchars'][0x0e32] = true;
6238  } else {
6239  $output[] = $ch0;
6240  }
6241  } elseif (in_array($ch1, $longtail)) {
6242  if ($ch0 == 0x0e31) {
6243  // lower left mai hun arkad
6244  $output[] = $this->replaceChar($ch0, 0xf710);
6245  } elseif (in_array($ch0, $upvowel)) {
6246  // lower left
6247  $output[] = $this->replaceChar($ch0, (0xf701 + $ch0 - 0x0e34));
6248  } elseif ($ch0 == 0x0e47) {
6249  // lower left mai tai koo
6250  $output[] = $this->replaceChar($ch0, 0xf712);
6251  } else {
6252  // normal character
6253  $output[] = $ch0;
6254  }
6255  } elseif (in_array($ch1, $lowtail) AND in_array($ch0, $lowvowel)) {
6256  // lower vowel
6257  $output[] = $this->replaceChar($ch0, (0xf718 + $ch0 - 0x0e38));
6258  } elseif (($ch0 == 0x0e0d) AND in_array($chn, $lowvowel)) {
6259  // yo ying without lower part
6260  $output[] = $this->replaceChar($ch0, 0xf70f);
6261  } elseif (($ch0 == 0x0e10) AND in_array($chn, $lowvowel)) {
6262  // tho santan without lower part
6263  $output[] = $this->replaceChar($ch0, 0xf700);
6264  } else {
6265  $output[] = $ch0;
6266  }
6267  } else {
6268  // non-thai character
6269  $output[] = $unicode[$i];
6270  }
6271  }
6272  $unicode = $output;
6273  // update font subsetchars
6274  $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
6275  } // end of K_THAI_TOPCHARS
6276  $txt2 = $this->arrUTF8ToUTF16BE($unicode, false);
6277  }
6278  }
6279  $txt2 = $this->_escape($txt2);
6280  // get current text width (considering general font stretching and spacing)
6281  $txwidth = $this->GetStringWidth($txt);
6282  $width = $txwidth;
6283  // check for stretch mode
6284  if ($stretch > 0) {
6285  // calculate ratio between cell width and text width
6286  if ($width <= 0) {
6287  $ratio = 1;
6288  } else {
6289  $ratio = (($w - $this->cell_padding['L'] - $this->cell_padding['R']) / $width);
6290  }
6291  // check if stretching is required
6292  if (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0))) {
6293  // the text will be stretched to fit cell width
6294  if ($stretch > 2) {
6295  // set new character spacing
6296  $this->font_spacing += ($w - $this->cell_padding['L'] - $this->cell_padding['R'] - $width) / (max(($this->GetNumChars($txt) - 1), 1) * ($this->font_stretching / 100));
6297  } else {
6298  // set new horizontal stretching
6299  $this->font_stretching *= $ratio;
6300  }
6301  // recalculate text width (the text fills the entire cell)
6302  $width = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
6303  // reset alignment
6304  $align = '';
6305  }
6306  }
6307  if ($this->font_stretching != 100) {
6308  // apply font stretching
6309  $rs .= sprintf('BT %F Tz ET ', $this->font_stretching);
6310  }
6311  if ($this->font_spacing != 0) {
6312  // increase/decrease font spacing
6313  $rs .= sprintf('BT %F Tc ET ', ($this->font_spacing * $this->k));
6314  }
6315  if ($this->ColorFlag AND ($this->textrendermode < 4)) {
6316  $s .= 'q '.$this->TextColor.' ';
6317  }
6318  // rendering mode
6319  $s .= sprintf('BT %d Tr %F w ET ', $this->textrendermode, ($this->textstrokewidth * $this->k));
6320  // count number of spaces
6321  $ns = substr_count($txt, chr(32));
6322  // Justification
6323  $spacewidth = 0;
6324  if (($align == 'J') AND ($ns > 0)) {
6325  if ($this->isUnicodeFont()) {
6326  // get string width without spaces
6327  $width = $this->GetStringWidth(str_replace(' ', '', $txt));
6328  // calculate average space width
6329  $spacewidth = -1000 * ($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1) / $this->FontSize;
6330  if ($this->font_stretching != 100) {
6331  // word spacing is affected by stretching
6332  $spacewidth /= ($this->font_stretching / 100);
6333  }
6334  // set word position to be used with TJ operator
6335  $txt2 = str_replace(chr(0).chr(32), ') '.sprintf('%F', $spacewidth).' (', $txt2);
6336  $unicode_justification = true;
6337  } else {
6338  // get string width
6339  $width = $txwidth;
6340  // new space width
6341  $spacewidth = (($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1)) * $this->k;
6342  if ($this->font_stretching != 100) {
6343  // word spacing (Tw) is affected by stretching
6344  $spacewidth /= ($this->font_stretching / 100);
6345  }
6346  // set word spacing
6347  $rs .= sprintf('BT %F Tw ET ', $spacewidth);
6348  }
6349  $width = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
6350  }
6351  // replace carriage return characters
6352  $txt2 = str_replace("\r", ' ', $txt2);
6353  switch ($align) {
6354  case 'C': {
6355  $dx = ($w - $width) / 2;
6356  break;
6357  }
6358  case 'R': {
6359  if ($this->rtl) {
6360  $dx = $this->cell_padding['R'];
6361  } else {
6362  $dx = $w - $width - $this->cell_padding['R'];
6363  }
6364  break;
6365  }
6366  case 'L': {
6367  if ($this->rtl) {
6368  $dx = $w - $width - $this->cell_padding['L'];
6369  } else {
6370  $dx = $this->cell_padding['L'];
6371  }
6372  break;
6373  }
6374  case 'J':
6375  default: {
6376  if ($this->rtl) {
6377  $dx = $this->cell_padding['R'];
6378  } else {
6379  $dx = $this->cell_padding['L'];
6380  }
6381  break;
6382  }
6383  }
6384  if ($this->rtl) {
6385  $xdx = $x - $dx - $width;
6386  } else {
6387  $xdx = $x + $dx;
6388  }
6389  $xdk = $xdx * $k;
6390  // print text
6391  $s .= sprintf('BT %F %F Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2);
6392  if (isset($uniblock)) {
6393  // print overlapping characters as separate string
6394  $xshift = 0; // horizontal shift
6395  $ty = (($this->h - $basefonty + (0.2 * $this->FontSize)) * $k);
6396  $spw = (($w - $txwidth - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1));
6397  foreach ($uniblock as $uk => $uniarr) {
6398  if (($uk % 2) == 0) {
6399  // x space to skip
6400  if ($spacewidth != 0) {
6401  // justification shift
6402  $xshift += (count(array_keys($uniarr, 32)) * $spw);
6403  }
6404  $xshift += $this->GetArrStringWidth($uniarr); // + shift justification
6405  } else {
6406  // character to print
6407  $topchr = $this->arrUTF8ToUTF16BE($uniarr, false);
6408  $topchr = $this->_escape($topchr);
6409  $s .= sprintf(' BT %F %F Td [(%s)] TJ ET', ($xdk + ($xshift * $k)), $ty, $topchr);
6410  }
6411  }
6412  }
6413  if ($this->underline) {
6414  $s .= ' '.$this->_dounderlinew($xdx, $basefonty, $width);
6415  }
6416  if ($this->linethrough) {
6417  $s .= ' '.$this->_dolinethroughw($xdx, $basefonty, $width);
6418  }
6419  if ($this->overline) {
6420  $s .= ' '.$this->_dooverlinew($xdx, $basefonty, $width);
6421  }
6422  if ($this->ColorFlag AND ($this->textrendermode < 4)) {
6423  $s .= ' Q';
6424  }
6425  if ($link) {
6426  $this->Link($xdx, $yt, $width, ($this->FontAscent + $this->FontDescent), $link, $ns);
6427  }
6428  }
6429  // output cell
6430  if ($s) {
6431  // output cell
6432  $rs .= $s;
6433  if ($this->font_spacing != 0) {
6434  // reset font spacing mode
6435  $rs .= ' BT 0 Tc ET';
6436  }
6437  if ($this->font_stretching != 100) {
6438  // reset font stretching mode
6439  $rs .= ' BT 100 Tz ET';
6440  }
6441  }
6442  // reset word spacing
6443  if (!$this->isUnicodeFont() AND ($align == 'J')) {
6444  $rs .= ' BT 0 Tw ET';
6445  }
6446  // reset stretching and spacing
6447  $this->font_stretching = $prev_font_stretching;
6448  $this->font_spacing = $prev_font_spacing;
6449  $this->lasth = $h;
6450  if ($ln > 0) {
6451  //Go to the beginning of the next line
6452  $this->y = $y + $h + $this->cell_margin['B'];
6453  if ($ln == 1) {
6454  if ($this->rtl) {
6455  $this->x = $this->w - $this->rMargin;
6456  } else {
6457  $this->x = $this->lMargin;
6458  }
6459  }
6460  } else {
6461  // go left or right by case
6462  if ($this->rtl) {
6463  $this->x = $x - $w - $this->cell_margin['L'];
6464  } else {
6465  $this->x = $x + $w + $this->cell_margin['R'];
6466  }
6467  }
6468  $gstyles = ''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor."\n";
6469  $rs = $gstyles.$rs;
6470  $this->cell_padding = $prev_cell_padding;
6471  $this->cell_margin = $prev_cell_margin;
6472  return $rs;
6473  }
6474 
6483  protected function replaceChar($oldchar, $newchar) {
6484  if ($this->isCharDefined($newchar)) {
6485  // add the new char on the subset list
6486  $this->CurrentFont['subsetchars'][$newchar] = true;
6487  // return the new character
6488  return $newchar;
6489  }
6490  // return the old char
6491  return $oldchar;
6492  }
6493 
6506  protected function getCellBorder($x, $y, $w, $h, $brd) {
6507  $s = ''; // string to be returned
6508  if (empty($brd)) {
6509  return $s;
6510  }
6511  if ($brd == 1) {
6512  $brd = array('LRTB' => true);
6513  }
6514  // calculate coordinates for border
6515  $k = $this->k;
6516  if ($this->rtl) {
6517  $xeL = ($x - $w) * $k;
6518  $xeR = $x * $k;
6519  } else {
6520  $xeL = $x * $k;
6521  $xeR = ($x + $w) * $k;
6522  }
6523  $yeL = (($this->h - ($y + $h)) * $k);
6524  $yeT = (($this->h - $y) * $k);
6525  $xeT = $xeL;
6526  $xeB = $xeR;
6527  $yeR = $yeT;
6528  $yeB = $yeL;
6529  if (is_string($brd)) {
6530  // convert string to array
6531  $slen = strlen($brd);
6532  $newbrd = array();
6533  for ($i = 0; $i < $slen; ++$i) {
6534  $newbrd[$brd[$i]] = array('cap' => 'square', 'join' => 'miter');
6535  }
6536  $brd = $newbrd;
6537  }
6538  if (isset($brd['mode'])) {
6539  $mode = $brd['mode'];
6540  unset($brd['mode']);
6541  } else {
6542  $mode = 'normal';
6543  }
6544  foreach ($brd as $border => $style) {
6545  if (is_array($style) AND !empty($style)) {
6546  // apply border style
6547  $prev_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' ';
6548  $s .= $this->SetLineStyle($style, true)."\n";
6549  }
6550  switch ($mode) {
6551  case 'ext': {
6552  $off = (($this->LineWidth / 2) * $k);
6553  $xL = $xeL - $off;
6554  $xR = $xeR + $off;
6555  $yT = $yeT + $off;
6556  $yL = $yeL - $off;
6557  $xT = $xL;
6558  $xB = $xR;
6559  $yR = $yT;
6560  $yB = $yL;
6561  $w += $this->LineWidth;
6562  $h += $this->LineWidth;
6563  break;
6564  }
6565  case 'int': {
6566  $off = ($this->LineWidth / 2) * $k;
6567  $xL = $xeL + $off;
6568  $xR = $xeR - $off;
6569  $yT = $yeT - $off;
6570  $yL = $yeL + $off;
6571  $xT = $xL;
6572  $xB = $xR;
6573  $yR = $yT;
6574  $yB = $yL;
6575  $w -= $this->LineWidth;
6576  $h -= $this->LineWidth;
6577  break;
6578  }
6579  case 'normal':
6580  default: {
6581  $xL = $xeL;
6582  $xT = $xeT;
6583  $xB = $xeB;
6584  $xR = $xeR;
6585  $yL = $yeL;
6586  $yT = $yeT;
6587  $yB = $yeB;
6588  $yR = $yeR;
6589  break;
6590  }
6591  }
6592  // draw borders by case
6593  if (strlen($border) == 4) {
6594  $s .= sprintf('%F %F %F %F re S ', $xT, $yT, ($w * $k), (-$h * $k));
6595  } elseif (strlen($border) == 3) {
6596  if (strpos($border,'B') === false) { // LTR
6597  $s .= sprintf('%F %F m ', $xL, $yL);
6598  $s .= sprintf('%F %F l ', $xT, $yT);
6599  $s .= sprintf('%F %F l ', $xR, $yR);
6600  $s .= sprintf('%F %F l ', $xB, $yB);
6601  $s .= 'S ';
6602  } elseif (strpos($border,'L') === false) { // TRB
6603  $s .= sprintf('%F %F m ', $xT, $yT);
6604  $s .= sprintf('%F %F l ', $xR, $yR);
6605  $s .= sprintf('%F %F l ', $xB, $yB);
6606  $s .= sprintf('%F %F l ', $xL, $yL);
6607  $s .= 'S ';
6608  } elseif (strpos($border,'T') === false) { // RBL
6609  $s .= sprintf('%F %F m ', $xR, $yR);
6610  $s .= sprintf('%F %F l ', $xB, $yB);
6611  $s .= sprintf('%F %F l ', $xL, $yL);
6612  $s .= sprintf('%F %F l ', $xT, $yT);
6613  $s .= 'S ';
6614  } elseif (strpos($border,'R') === false) { // BLT
6615  $s .= sprintf('%F %F m ', $xB, $yB);
6616  $s .= sprintf('%F %F l ', $xL, $yL);
6617  $s .= sprintf('%F %F l ', $xT, $yT);
6618  $s .= sprintf('%F %F l ', $xR, $yR);
6619  $s .= 'S ';
6620  }
6621  } elseif (strlen($border) == 2) {
6622  if ((strpos($border,'L') !== false) AND (strpos($border,'T') !== false)) { // LT
6623  $s .= sprintf('%F %F m ', $xL, $yL);
6624  $s .= sprintf('%F %F l ', $xT, $yT);
6625  $s .= sprintf('%F %F l ', $xR, $yR);
6626  $s .= 'S ';
6627  } elseif ((strpos($border,'T') !== false) AND (strpos($border,'R') !== false)) { // TR
6628  $s .= sprintf('%F %F m ', $xT, $yT);
6629  $s .= sprintf('%F %F l ', $xR, $yR);
6630  $s .= sprintf('%F %F l ', $xB, $yB);
6631  $s .= 'S ';
6632  } elseif ((strpos($border,'R') !== false) AND (strpos($border,'B') !== false)) { // RB
6633  $s .= sprintf('%F %F m ', $xR, $yR);
6634  $s .= sprintf('%F %F l ', $xB, $yB);
6635  $s .= sprintf('%F %F l ', $xL, $yL);
6636  $s .= 'S ';
6637  } elseif ((strpos($border,'B') !== false) AND (strpos($border,'L') !== false)) { // BL
6638  $s .= sprintf('%F %F m ', $xB, $yB);
6639  $s .= sprintf('%F %F l ', $xL, $yL);
6640  $s .= sprintf('%F %F l ', $xT, $yT);
6641  $s .= 'S ';
6642  } elseif ((strpos($border,'L') !== false) AND (strpos($border,'R') !== false)) { // LR
6643  $s .= sprintf('%F %F m ', $xL, $yL);
6644  $s .= sprintf('%F %F l ', $xT, $yT);
6645  $s .= 'S ';
6646  $s .= sprintf('%F %F m ', $xR, $yR);
6647  $s .= sprintf('%F %F l ', $xB, $yB);
6648  $s .= 'S ';
6649  } elseif ((strpos($border,'T') !== false) AND (strpos($border,'B') !== false)) { // TB
6650  $s .= sprintf('%F %F m ', $xT, $yT);
6651  $s .= sprintf('%F %F l ', $xR, $yR);
6652  $s .= 'S ';
6653  $s .= sprintf('%F %F m ', $xB, $yB);
6654  $s .= sprintf('%F %F l ', $xL, $yL);
6655  $s .= 'S ';
6656  }
6657  } else { // strlen($border) == 1
6658  if (strpos($border,'L') !== false) { // L
6659  $s .= sprintf('%F %F m ', $xL, $yL);
6660  $s .= sprintf('%F %F l ', $xT, $yT);
6661  $s .= 'S ';
6662  } elseif (strpos($border,'T') !== false) { // T
6663  $s .= sprintf('%F %F m ', $xT, $yT);
6664  $s .= sprintf('%F %F l ', $xR, $yR);
6665  $s .= 'S ';
6666  } elseif (strpos($border,'R') !== false) { // R
6667  $s .= sprintf('%F %F m ', $xR, $yR);
6668  $s .= sprintf('%F %F l ', $xB, $yB);
6669  $s .= 'S ';
6670  } elseif (strpos($border,'B') !== false) { // B
6671  $s .= sprintf('%F %F m ', $xB, $yB);
6672  $s .= sprintf('%F %F l ', $xL, $yL);
6673  $s .= 'S ';
6674  }
6675  }
6676  if (is_array($style) AND !empty($style)) {
6677  // reset border style to previous value
6678  $s .= "\n".$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor."\n";
6679  }
6680  }
6681  return $s;
6682  }
6683 
6709  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) {
6710  $prev_cell_margin = $this->cell_margin;
6711  $prev_cell_padding = $this->cell_padding;
6712  // adjust internal padding
6713  $this->adjustCellPadding($border);
6714  $mc_padding = $this->cell_padding;
6715  $mc_margin = $this->cell_margin;
6716  $this->cell_padding['T'] = 0;
6717  $this->cell_padding['B'] = 0;
6718  $this->setCellMargins(0, 0, 0, 0);
6719  if ($this->empty_string($this->lasth) OR $reseth) {
6720  // reset row height
6721  $this->resetLastH();
6722  }
6723  if (!$this->empty_string($y)) {
6724  $this->SetY($y);
6725  } else {
6726  $y = $this->GetY();
6727  }
6728  $resth = 0;
6729  if (($h > 0) AND $this->inPageBody() AND (($y + $h + $mc_margin['T'] + $mc_margin['B']) > $this->PageBreakTrigger)) {
6730  // spit cell in more pages/columns
6731  $newh = ($this->PageBreakTrigger - $y);
6732  $resth = ($h - $newh); // cell to be printed on the next page/column
6733  $h = $newh;
6734  }
6735  // get current page number
6736  $startpage = $this->page;
6737  // get current column
6738  $startcolumn = $this->current_column;
6739  if (!$this->empty_string($x)) {
6740  $this->SetX($x);
6741  } else {
6742  $x = $this->GetX();
6743  }
6744  // check page for no-write regions and adapt page margins if necessary
6745  list($x, $y) = $this->checkPageRegions(0, $x, $y);
6746  // apply margins
6747  $oy = $y + $mc_margin['T'];
6748  if ($this->rtl) {
6749  $ox = ($this->w - $x - $mc_margin['R']);
6750  } else {
6751  $ox = ($x + $mc_margin['L']);
6752  }
6753  $this->x = $ox;
6754  $this->y = $oy;
6755  // set width
6756  if ($this->empty_string($w) OR ($w <= 0)) {
6757  if ($this->rtl) {
6758  $w = ($this->x - $this->lMargin - $mc_margin['L']);
6759  } else {
6760  $w = ($this->w - $this->x - $this->rMargin - $mc_margin['R']);
6761  }
6762  }
6763  // store original margin values
6766  if ($this->rtl) {
6767  $this->rMargin = ($this->w - $this->x);
6768  $this->lMargin = ($this->x - $w);
6769  } else {
6770  $this->lMargin = ($this->x);
6771  $this->rMargin = ($this->w - $this->x - $w);
6772  }
6773  $this->clMargin = $this->lMargin;
6774  $this->crMargin = $this->rMargin;
6775  if ($autopadding) {
6776  // add top padding
6777  $this->y += $mc_padding['T'];
6778  }
6779  if ($ishtml) { // ******* Write HTML text
6780  $this->writeHTML($txt, true, false, $reseth, true, $align);
6781  $nl = 1;
6782  } else { // ******* Write simple text
6783  $prev_FontSizePt = $this->FontSizePt;
6784  // vertical alignment
6785  if ($maxh > 0) {
6786  // get text height
6787  $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
6788  if ($fitcell) {
6789  // try to reduce font size to fit text on cell (use a quick search algorithm)
6790  $fmin = 1;
6791  $fmax = $this->FontSizePt;
6792  $prev_text_height = $text_height;
6793  $maxit = 100; // max number of iterations
6794  while ($maxit > 0) {
6795  $fmid = (($fmax + $fmin) / 2);
6796  $this->SetFontSize($fmid, false);
6797  $this->resetLastH();
6798  $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
6799  if (($text_height == $maxh) OR (($text_height < $maxh) AND ($fmin >= ($fmax - 0.01)))) {
6800  break;
6801  } elseif ($text_height < $maxh) {
6802  $fmin = $fmid;
6803  } else {
6804  $fmax = $fmid;
6805  }
6806  --$maxit;
6807  }
6808  $this->SetFontSize($this->FontSizePt);
6809  }
6810  if ($text_height < $maxh) {
6811  if ($valign == 'M') {
6812  // text vertically centered
6813  $this->y += (($maxh - $text_height) / 2);
6814  } elseif ($valign == 'B') {
6815  // text vertically aligned on bottom
6816  $this->y += ($maxh - $text_height);
6817  }
6818  }
6819  }
6820  $nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false, true, $maxh, 0, $mc_margin);
6821  if ($fitcell) {
6822  // restore font size
6823  $this->SetFontSize($prev_FontSizePt);
6824  }
6825  }
6826  if ($autopadding) {
6827  // add bottom padding
6828  $this->y += $mc_padding['B'];
6829  }
6830  // Get end-of-text Y position
6831  $currentY = $this->y;
6832  // get latest page number
6833  $endpage = $this->page;
6834  if ($resth > 0) {
6835  $skip = ($endpage - $startpage);
6836  $tmpresth = $resth;
6837  while ($tmpresth > 0) {
6838  if ($skip <= 0) {
6839  // add a page (or trig AcceptPageBreak() for multicolumn mode)
6840  $this->checkPageBreak($this->PageBreakTrigger + 1);
6841  }
6842  if ($this->num_columns > 1) {
6843  $tmpresth -= ($this->h - $this->y - $this->bMargin);
6844  } else {
6845  $tmpresth -= ($this->h - $this->tMargin - $this->bMargin);
6846  }
6847  --$skip;
6848  }
6849  $currentY = $this->y;
6850  $endpage = $this->page;
6851  }
6852  // get latest column
6853  $endcolumn = $this->current_column;
6854  if ($this->num_columns == 0) {
6855  $this->num_columns = 1;
6856  }
6857  // disable page regions check
6859  $this->check_page_regions = false;
6860  // get border modes
6861  $border_start = $this->getBorderMode($border, $position='start');
6862  $border_end = $this->getBorderMode($border, $position='end');
6863  $border_middle = $this->getBorderMode($border, $position='middle');
6864  // design borders around HTML cells.
6865  for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
6866  $ccode = '';
6867  $this->setPage($page);
6868  if ($this->num_columns < 2) {
6869  // single-column mode
6870  $this->SetX($x);
6871  $this->y = $this->tMargin;
6872  }
6873  // account for margin changes
6874  if ($page > $startpage) {
6875  if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
6876  $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
6877  } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
6878  $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
6879  }
6880  }
6881  if ($startpage == $endpage) {
6882  // single page
6883  for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
6884  $this->selectColumn($column);
6885  if ($this->rtl) {
6886  $this->x -= $mc_margin['R'];
6887  } else {
6888  $this->x += $mc_margin['L'];
6889  }
6890  if ($startcolumn == $endcolumn) { // single column
6891  $cborder = $border;
6892  $h = max($h, ($currentY - $oy));
6893  $this->y = $oy;
6894  } elseif ($column == $startcolumn) { // first column
6895  $cborder = $border_start;
6896  $this->y = $oy;
6897  $h = $this->h - $this->y - $this->bMargin;
6898  } elseif ($column == $endcolumn) { // end column
6899  $cborder = $border_end;
6900  $h = $currentY - $this->y;
6901  if ($resth > $h) {
6902  $h = $resth;
6903  }
6904  } else { // middle column
6905  $cborder = $border_middle;
6906  $h = $this->h - $this->y - $this->bMargin;
6907  $resth -= $h;
6908  }
6909  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6910  } // end for each column
6911  } elseif ($page == $startpage) { // first page
6912  for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
6913  $this->selectColumn($column);
6914  if ($this->rtl) {
6915  $this->x -= $mc_margin['R'];
6916  } else {
6917  $this->x += $mc_margin['L'];
6918  }
6919  if ($column == $startcolumn) { // first column
6920  $cborder = $border_start;
6921  $this->y = $oy;
6922  $h = $this->h - $this->y - $this->bMargin;
6923  } else { // middle column
6924  $cborder = $border_middle;
6925  $h = $this->h - $this->y - $this->bMargin;
6926  $resth -= $h;
6927  }
6928  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6929  } // end for each column
6930  } elseif ($page == $endpage) { // last page
6931  for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
6932  $this->selectColumn($column);
6933  if ($this->rtl) {
6934  $this->x -= $mc_margin['R'];
6935  } else {
6936  $this->x += $mc_margin['L'];
6937  }
6938  if ($column == $endcolumn) {
6939  // end column
6940  $cborder = $border_end;
6941  $h = $currentY - $this->y;
6942  if ($resth > $h) {
6943  $h = $resth;
6944  }
6945  } else {
6946  // middle column
6947  $cborder = $border_middle;
6948  $h = $this->h - $this->y - $this->bMargin;
6949  $resth -= $h;
6950  }
6951  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6952  } // end for each column
6953  } else { // middle page
6954  for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
6955  $this->selectColumn($column);
6956  if ($this->rtl) {
6957  $this->x -= $mc_margin['R'];
6958  } else {
6959  $this->x += $mc_margin['L'];
6960  }
6961  $cborder = $border_middle;
6962  $h = $this->h - $this->y - $this->bMargin;
6963  $resth -= $h;
6964  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6965  } // end for each column
6966  }
6967  if ($cborder OR $fill) {
6968  $offsetlen = strlen($ccode);
6969  // draw border and fill
6970  if ($this->inxobj) {
6971  // we are inside an XObject template
6972  if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
6973  $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
6974  $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
6975  $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
6976  } else {
6977  $pagemark = $this->xobjects[$this->xobjid]['intmrk'];
6978  $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
6979  }
6980  $pagebuff = $this->xobjects[$this->xobjid]['outdata'];
6981  $pstart = substr($pagebuff, 0, $pagemark);
6982  $pend = substr($pagebuff, $pagemark);
6983  $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
6984  } else {
6985  if (end($this->transfmrk[$this->page]) !== false) {
6986  $pagemarkkey = key($this->transfmrk[$this->page]);
6987  $pagemark = $this->transfmrk[$this->page][$pagemarkkey];
6988  $this->transfmrk[$this->page][$pagemarkkey] += $offsetlen;
6989  } elseif ($this->InFooter) {
6990  $pagemark = $this->footerpos[$this->page];
6991  $this->footerpos[$this->page] += $offsetlen;
6992  } else {
6993  $pagemark = $this->intmrk[$this->page];
6994  $this->intmrk[$this->page] += $offsetlen;
6995  }
6996  $pagebuff = $this->getPageBuffer($this->page);
6997  $pstart = substr($pagebuff, 0, $pagemark);
6998  $pend = substr($pagebuff, $pagemark);
6999  $this->setPageBuffer($this->page, $pstart.$ccode.$pend);
7000  }
7001  }
7002  } // end for each page
7003  // restore page regions check
7004  $this->check_page_regions = $check_page_regions;
7005  // Get end-of-cell Y position
7006  $currentY = $this->GetY();
7007  // restore previous values
7008  if ($this->num_columns > 1) {
7009  $this->selectColumn();
7010  } else {
7011  // restore original margins
7012  $this->lMargin = $lMargin;
7013  $this->rMargin = $rMargin;
7014  if ($this->page > $startpage) {
7015  // check for margin variations between pages (i.e. booklet mode)
7016  $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$startpage]['olm']);
7017  $dr = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$startpage]['orm']);
7018  if (($dl != 0) OR ($dr != 0)) {
7019  $this->lMargin += $dl;
7020  $this->rMargin += $dr;
7021  }
7022  }
7023  }
7024  if ($ln > 0) {
7025  //Go to the beginning of the next line
7026  $this->SetY($currentY + $mc_margin['B']);
7027  if ($ln == 2) {
7028  $this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']);
7029  }
7030  } else {
7031  // go left or right by case
7032  $this->setPage($startpage);
7033  $this->y = $y;
7034  $this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']);
7035  }
7036  $this->setContentMark();
7037  $this->cell_padding = $prev_cell_padding;
7038  $this->cell_margin = $prev_cell_margin;
7039  $this->clMargin = $this->lMargin;
7040  $this->crMargin = $this->rMargin;
7041  return $nl;
7042  }
7043 
7052  protected function getBorderMode($brd, $position='start') {
7053  if ((!$this->opencell) OR empty($brd)) {
7054  return $brd;
7055  }
7056  if ($brd == 1) {
7057  $brd = 'LTRB';
7058  }
7059  if (is_string($brd)) {
7060  // convert string to array
7061  $slen = strlen($brd);
7062  $newbrd = array();
7063  for ($i = 0; $i < $slen; ++$i) {
7064  $newbrd[$brd[$i]] = array('cap' => 'square', 'join' => 'miter');
7065  }
7066  $brd = $newbrd;
7067  }
7068  foreach ($brd as $border => $style) {
7069  switch ($position) {
7070  case 'start': {
7071  if (strpos($border, 'B') !== false) {
7072  // remove bottom line
7073  $newkey = str_replace('B', '', $border);
7074  if (strlen($newkey) > 0) {
7075  $brd[$newkey] = $style;
7076  }
7077  unset($brd[$border]);
7078  }
7079  break;
7080  }
7081  case 'middle': {
7082  if (strpos($border, 'B') !== false) {
7083  // remove bottom line
7084  $newkey = str_replace('B', '', $border);
7085  if (strlen($newkey) > 0) {
7086  $brd[$newkey] = $style;
7087  }
7088  unset($brd[$border]);
7089  $border = $newkey;
7090  }
7091  if (strpos($border, 'T') !== false) {
7092  // remove bottom line
7093  $newkey = str_replace('T', '', $border);
7094  if (strlen($newkey) > 0) {
7095  $brd[$newkey] = $style;
7096  }
7097  unset($brd[$border]);
7098  }
7099  break;
7100  }
7101  case 'end': {
7102  if (strpos($border, 'T') !== false) {
7103  // remove bottom line
7104  $newkey = str_replace('T', '', $border);
7105  if (strlen($newkey) > 0) {
7106  $brd[$newkey] = $style;
7107  }
7108  unset($brd[$border]);
7109  }
7110  break;
7111  }
7112  }
7113  }
7114  return $brd;
7115  }
7116 
7130  public function getNumLines($txt, $w=0, $reseth=false, $autopadding=true, $cellpadding='', $border=0) {
7131  if ($txt === '') {
7132  // empty string
7133  return 1;
7134  }
7135  // adjust internal padding
7136  $prev_cell_padding = $this->cell_padding;
7137  $prev_lasth = $this->lasth;
7138  if (is_array($cellpadding)) {
7139  $this->cell_padding = $cellpadding;
7140  }
7141  $this->adjustCellPadding($border);
7142  if ($this->empty_string($w) OR ($w <= 0)) {
7143  if ($this->rtl) {
7144  $w = $this->x - $this->lMargin;
7145  } else {
7146  $w = $this->w - $this->rMargin - $this->x;
7147  }
7148  }
7149  $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
7150  if ($reseth) {
7151  // reset row height
7152  $this->resetLastH();
7153  }
7154  $lines = 1;
7155  $sum = 0;
7156  $chars = $this->utf8Bidi($this->UTF8StringToArray($txt), $txt, $this->tmprtl);
7157  $charsWidth = $this->GetArrStringWidth($chars, '', '', 0, true);
7158  $length = count($chars);
7159  $lastSeparator = -1;
7160  for ($i = 0; $i < $length; ++$i) {
7161  $charWidth = $charsWidth[$i];
7162  if (preg_match($this->re_spaces, $this->unichr($chars[$i]))) {
7163  $lastSeparator = $i;
7164  }
7165  if ((($sum + $charWidth) > $wmax) OR ($chars[$i] == 10)) {
7166  ++$lines;
7167  if ($chars[$i] == 10) {
7168  $lastSeparator = -1;
7169  $sum = 0;
7170  } elseif ($lastSeparator != -1) {
7171  $i = $lastSeparator;
7172  $lastSeparator = -1;
7173  $sum = 0;
7174  } else {
7175  $sum = $charWidth;
7176  }
7177  } else {
7178  $sum += $charWidth;
7179  }
7180  }
7181  if ($chars[($length - 1)] == 10) {
7182  --$lines;
7183  }
7184  $this->cell_padding = $prev_cell_padding;
7185  $this->lasth = $prev_lasth;
7186  return $lines;
7187  }
7188 
7236  public function getStringHeight($w, $txt, $reseth=false, $autopadding=true, $cellpadding='', $border=0) {
7237  // adjust internal padding
7238  $prev_cell_padding = $this->cell_padding;
7239  $prev_lasth = $this->lasth;
7240  if (is_array($cellpadding)) {
7241  $this->cell_padding = $cellpadding;
7242  }
7243  $this->adjustCellPadding($border);
7244  $lines = $this->getNumLines($txt, $w, $reseth, $autopadding, $cellpadding, $border);
7245  $height = $lines * ($this->FontSize * $this->cell_height_ratio);
7246  if ($autopadding) {
7247  // add top and bottom padding
7248  $height += ($this->cell_padding['T'] + $this->cell_padding['B']);
7249  }
7250  $this->cell_padding = $prev_cell_padding;
7251  $this->lasth = $prev_lasth;
7252  return $height;
7253  }
7254 
7273  public function Write($h, $txt, $link='', $fill=false, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0, $wadj=0, $margin='') {
7274  // check page for no-write regions and adapt page margins if necessary
7275  list($this->x, $this->y) = $this->checkPageRegions($h, $this->x, $this->y);
7276  if (strlen($txt) == 0) {
7277  // fix empty text
7278  $txt = ' ';
7279  }
7280  if ($margin === '') {
7281  // set default margins
7282  $margin = $this->cell_margin;
7283  }
7284  // remove carriage returns
7285  $s = str_replace("\r", '', $txt);
7286  // check if string contains arabic text
7287  if (preg_match($this->unicode->uni_RE_PATTERN_ARABIC, $s)) {
7288  $arabic = true;
7289  } else {
7290  $arabic = false;
7291  }
7292  // check if string contains RTL text
7293  if ($arabic OR ($this->tmprtl == 'R') OR preg_match($this->unicode->uni_RE_PATTERN_RTL, $s)) {
7294  $rtlmode = true;
7295  } else {
7296  $rtlmode = false;
7297  }
7298  // get a char width
7299  $chrwidth = $this->GetCharWidth(46); // dot character
7300  // get array of unicode values
7301  $chars = $this->UTF8StringToArray($s);
7302  // calculate maximum width for a single character on string
7303  $chrw = $this->GetArrStringWidth($chars, '', '', 0, true);
7304  array_walk($chrw, array($this, 'getRawCharWidth'));
7305  $maxchwidth = max($chrw);
7306  // get array of chars
7307  $uchars = $this->UTF8ArrayToUniArray($chars);
7308  // get the number of characters
7309  $nb = count($chars);
7310  // replacement for SHY character (minus symbol)
7311  $shy_replacement = 45;
7312  $shy_replacement_char = $this->unichr($shy_replacement);
7313  // widht for SHY replacement
7314  $shy_replacement_width = $this->GetCharWidth($shy_replacement);
7315  // max Y
7316  $maxy = $this->y + $maxh - $h - $this->cell_padding['T'] - $this->cell_padding['B'];
7317  // page width
7318  $pw = $w = $this->w - $this->lMargin - $this->rMargin;
7319  // calculate remaining line width ($w)
7320  if ($this->rtl) {
7321  $w = $this->x - $this->lMargin;
7322  } else {
7323  $w = $this->w - $this->rMargin - $this->x;
7324  }
7325  // max column width
7326  $wmax = ($w - $wadj);
7327  if (!$firstline) {
7328  $wmax -= ($this->cell_padding['L'] + $this->cell_padding['R']);
7329  }
7330  if ((!$firstline) AND (($chrwidth > $wmax) OR ($maxchwidth > $wmax))) {
7331  // the maximum width character do not fit on column
7332  return '';
7333  }
7334  // minimum row height
7335  $row_height = max($h, $this->FontSize * $this->cell_height_ratio);
7336  $start_page = $this->page;
7337  $i = 0; // character position
7338  $j = 0; // current starting position
7339  $sep = -1; // position of the last blank space
7340  $shy = false; // true if the last blank is a soft hypen (SHY)
7341  $l = 0; // current string length
7342  $nl = 0; //number of lines
7343  $linebreak = false;
7344  $pc = 0; // previous character
7345  // for each character
7346  while ($i < $nb) {
7347  if (($maxh > 0) AND ($this->y >= $maxy) ) {
7348  break;
7349  }
7350  //Get the current character
7351  $c = $chars[$i];
7352  if ($c == 10) { // 10 = "\n" = new line
7353  //Explicit line break
7354  if ($align == 'J') {
7355  if ($this->rtl) {
7356  $talign = 'R';
7357  } else {
7358  $talign = 'L';
7359  }
7360  } else {
7361  $talign = $align;
7362  }
7363  $tmpstr = $this->UniArrSubString($uchars, $j, $i);
7364  if ($firstline) {
7365  $startx = $this->x;
7366  $tmparr = array_slice($chars, $j, ($i - $j));
7367  if ($rtlmode) {
7368  $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
7369  }
7370  $linew = $this->GetArrStringWidth($tmparr);
7371  unset($tmparr);
7372  if ($this->rtl) {
7373  $this->endlinex = $startx - $linew;
7374  } else {
7375  $this->endlinex = $startx + $linew;
7376  }
7377  $w = $linew;
7378  $tmpcellpadding = $this->cell_padding;
7379  if ($maxh == 0) {
7380  $this->SetCellPadding(0);
7381  }
7382  }
7383  if ($firstblock AND $this->isRTLTextDir()) {
7384  $tmpstr = $this->stringRightTrim($tmpstr);
7385  }
7386  // Skip newlines at the begining of a page or column
7387  if (!empty($tmpstr) OR ($this->y < ($this->PageBreakTrigger - $row_height))) {
7388  $this->Cell($w, $h, $tmpstr, 0, 1, $talign, $fill, $link, $stretch);
7389  }
7390  unset($tmpstr);
7391  if ($firstline) {
7392  $this->cell_padding = $tmpcellpadding;
7393  return ($this->UniArrSubString($uchars, $i));
7394  }
7395  ++$nl;
7396  $j = $i + 1;
7397  $l = 0;
7398  $sep = -1;
7399  $shy = false;
7400  // account for margin changes
7401  if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) {
7402  $this->AcceptPageBreak();
7403  if ($this->rtl) {
7404  $this->x -= $margin['R'];
7405  } else {
7406  $this->x += $margin['L'];
7407  }
7408  $this->lMargin += $margin['L'];
7409  $this->rMargin += $margin['R'];
7410  }
7411  $w = $this->getRemainingWidth();
7412  $wmax = ($w - $this->cell_padding['L'] - $this->cell_padding['R']);
7413  } else {
7414  // 160 is the non-breaking space.
7415  // 173 is SHY (Soft Hypen).
7416  // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
7417  // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
7418  // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
7419  if (($c != 160)
7420  AND (($c == 173)
7421  OR preg_match($this->re_spaces, $this->unichr($c))
7422  OR (($c == 45)
7423  AND ($i < ($nb - 1))
7424  AND @preg_match('/[\p{L}]/'.$this->re_space['m'], $this->unichr($pc))
7425  AND @preg_match('/[\p{L}]/'.$this->re_space['m'], $this->unichr($chars[($i + 1)]))
7426  )
7427  )
7428  ) {
7429  // update last blank space position
7430  $sep = $i;
7431  // check if is a SHY
7432  if (($c == 173) OR ($c == 45)) {
7433  $shy = true;
7434  if ($pc == 45) {
7435  $tmp_shy_replacement_width = 0;
7436  $tmp_shy_replacement_char = '';
7437  } else {
7438  $tmp_shy_replacement_width = $shy_replacement_width;
7439  $tmp_shy_replacement_char = $shy_replacement_char;
7440  }
7441  } else {
7442  $shy = false;
7443  }
7444  }
7445  // update string length
7446  if ($this->isUnicodeFont() AND ($arabic)) {
7447  // with bidirectional algorithm some chars may be changed affecting the line length
7448  // *** very slow ***
7449  $l = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, ($i - $j)), '', $this->tmprtl));
7450  } else {
7451  $l += $this->GetCharWidth($c);
7452  }
7453  if (($l > $wmax) OR (($c == 173) AND (($l + $tmp_shy_replacement_width) > $wmax)) ) {
7454  // we have reached the end of column
7455  if ($sep == -1) {
7456  // check if the line was already started
7457  if (($this->rtl AND ($this->x <= ($this->w - $this->rMargin - $this->cell_padding['R'] - $margin['R'] - $chrwidth)))
7458  OR ((!$this->rtl) AND ($this->x >= ($this->lMargin + $this->cell_padding['L'] + $margin['L'] + $chrwidth)))) {
7459  // print a void cell and go to next line
7460  $this->Cell($w, $h, '', 0, 1);
7461  $linebreak = true;
7462  if ($firstline) {
7463  return ($this->UniArrSubString($uchars, $j));
7464  }
7465  } else {
7466  // truncate the word because do not fit on column
7467  $tmpstr = $this->UniArrSubString($uchars, $j, $i);
7468  if ($firstline) {
7469  $startx = $this->x;
7470  $tmparr = array_slice($chars, $j, ($i - $j));
7471  if ($rtlmode) {
7472  $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
7473  }
7474  $linew = $this->GetArrStringWidth($tmparr);
7475  unset($tmparr);
7476  if ($this->rtl) {
7477  $this->endlinex = $startx - $linew;
7478  } else {
7479  $this->endlinex = $startx + $linew;
7480  }
7481  $w = $linew;
7482  $tmpcellpadding = $this->cell_padding;
7483  if ($maxh == 0) {
7484  $this->SetCellPadding(0);
7485  }
7486  }
7487  if ($firstblock AND $this->isRTLTextDir()) {
7488  $tmpstr = $this->stringRightTrim($tmpstr);
7489  }
7490  $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
7491  unset($tmpstr);
7492  if ($firstline) {
7493  $this->cell_padding = $tmpcellpadding;
7494  return ($this->UniArrSubString($uchars, $i));
7495  }
7496  $j = $i;
7497  --$i;
7498  }
7499  } else {
7500  // word wrapping
7501  if ($this->rtl AND (!$firstblock) AND ($sep < $i)) {
7502  $endspace = 1;
7503  } else {
7504  $endspace = 0;
7505  }
7506  // check the length of the next string
7507  $strrest = $this->UniArrSubString($uchars, ($sep + $endspace));
7508  $nextstr = preg_split('/'.$this->re_space['p'].'/'.$this->re_space['m'], $this->stringTrim($strrest));
7509  if (isset($nextstr[0]) AND ($this->GetStringWidth($nextstr[0]) > $pw)) {
7510  // truncate the word because do not fit on a full page width
7511  $tmpstr = $this->UniArrSubString($uchars, $j, $i);
7512  if ($firstline) {
7513  $startx = $this->x;
7514  $tmparr = array_slice($chars, $j, ($i - $j));
7515  if ($rtlmode) {
7516  $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
7517  }
7518  $linew = $this->GetArrStringWidth($tmparr);
7519  unset($tmparr);
7520  if ($this->rtl) {
7521  $this->endlinex = ($startx - $linew);
7522  } else {
7523  $this->endlinex = ($startx + $linew);
7524  }
7525  $w = $linew;
7526  $tmpcellpadding = $this->cell_padding;
7527  if ($maxh == 0) {
7528  $this->SetCellPadding(0);
7529  }
7530  }
7531  if ($firstblock AND $this->isRTLTextDir()) {
7532  $tmpstr = $this->stringRightTrim($tmpstr);
7533  }
7534  $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
7535  unset($tmpstr);
7536  if ($firstline) {
7537  $this->cell_padding = $tmpcellpadding;
7538  return ($this->UniArrSubString($uchars, $i));
7539  }
7540  $j = $i;
7541  --$i;
7542  } else {
7543  // word wrapping
7544  if ($shy) {
7545  // add hypen (minus symbol) at the end of the line
7546  $shy_width = $tmp_shy_replacement_width;
7547  if ($this->rtl) {
7548  $shy_char_left = $tmp_shy_replacement_char;
7549  $shy_char_right = '';
7550  } else {
7551  $shy_char_left = '';
7552  $shy_char_right = $tmp_shy_replacement_char;
7553  }
7554  } else {
7555  $shy_width = 0;
7556  $shy_char_left = '';
7557  $shy_char_right = '';
7558  }
7559  $tmpstr = $this->UniArrSubString($uchars, $j, ($sep + $endspace));
7560  if ($firstline) {
7561  $startx = $this->x;
7562  $tmparr = array_slice($chars, $j, (($sep + $endspace) - $j));
7563  if ($rtlmode) {
7564  $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
7565  }
7566  $linew = $this->GetArrStringWidth($tmparr);
7567  unset($tmparr);
7568  if ($this->rtl) {
7569  $this->endlinex = $startx - $linew - $shy_width;
7570  } else {
7571  $this->endlinex = $startx + $linew + $shy_width;
7572  }
7573  $w = $linew;
7574  $tmpcellpadding = $this->cell_padding;
7575  if ($maxh == 0) {
7576  $this->SetCellPadding(0);
7577  }
7578  }
7579  // print the line
7580  if ($firstblock AND $this->isRTLTextDir()) {
7581  $tmpstr = $this->stringRightTrim($tmpstr);
7582  }
7583  $this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch);
7584  unset($tmpstr);
7585  if ($firstline) {
7586  if ($chars[$sep] == 45) {
7587  $endspace += 1;
7588  }
7589  // return the remaining text
7590  $this->cell_padding = $tmpcellpadding;
7591  return ($this->UniArrSubString($uchars, ($sep + $endspace)));
7592  }
7593  $i = $sep;
7594  $sep = -1;
7595  $shy = false;
7596  $j = ($i + 1);
7597  }
7598  }
7599  // account for margin changes
7600  if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) {
7601  $this->AcceptPageBreak();
7602  if ($this->rtl) {
7603  $this->x -= $margin['R'];
7604  } else {
7605  $this->x += $margin['L'];
7606  }
7607  $this->lMargin += $margin['L'];
7608  $this->rMargin += $margin['R'];
7609  }
7610  $w = $this->getRemainingWidth();
7611  $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
7612  if ($linebreak) {
7613  $linebreak = false;
7614  } else {
7615  ++$nl;
7616  $l = 0;
7617  }
7618  }
7619  }
7620  // save last character
7621  $pc = $c;
7622  ++$i;
7623  } // end while i < nb
7624  // print last substring (if any)
7625  if ($l > 0) {
7626  switch ($align) {
7627  case 'J':
7628  case 'C': {
7629  $w = $w;
7630  break;
7631  }
7632  case 'L': {
7633  if ($this->rtl) {
7634  $w = $w;
7635  } else {
7636  $w = $l;
7637  }
7638  break;
7639  }
7640  case 'R': {
7641  if ($this->rtl) {
7642  $w = $l;
7643  } else {
7644  $w = $w;
7645  }
7646  break;
7647  }
7648  default: {
7649  $w = $l;
7650  break;
7651  }
7652  }
7653  $tmpstr = $this->UniArrSubString($uchars, $j, $nb);
7654  if ($firstline) {
7655  $startx = $this->x;
7656  $tmparr = array_slice($chars, $j, ($nb - $j));
7657  if ($rtlmode) {
7658  $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
7659  }
7660  $linew = $this->GetArrStringWidth($tmparr);
7661  unset($tmparr);
7662  if ($this->rtl) {
7663  $this->endlinex = $startx - $linew;
7664  } else {
7665  $this->endlinex = $startx + $linew;
7666  }
7667  $w = $linew;
7668  $tmpcellpadding = $this->cell_padding;
7669  if ($maxh == 0) {
7670  $this->SetCellPadding(0);
7671  }
7672  }
7673  if ($firstblock AND $this->isRTLTextDir()) {
7674  $tmpstr = $this->stringRightTrim($tmpstr);
7675  }
7676  $this->Cell($w, $h, $tmpstr, 0, $ln, $align, $fill, $link, $stretch);
7677  unset($tmpstr);
7678  if ($firstline) {
7679  $this->cell_padding = $tmpcellpadding;
7680  return ($this->UniArrSubString($uchars, $nb));
7681  }
7682  ++$nl;
7683  }
7684  if ($firstline) {
7685  return '';
7686  }
7687  return $nl;
7688  }
7689 
7695  protected function getRemainingWidth() {
7696  list($this->x, $this->y) = $this->checkPageRegions(0, $this->x, $this->y);
7697  if ($this->rtl) {
7698  return ($this->x - $this->lMargin);
7699  } else {
7700  return ($this->w - $this->rMargin - $this->x);
7701  }
7702  }
7703 
7712  public function UTF8ArrSubString($strarr, $start='', $end='') {
7713  if (strlen($start) == 0) {
7714  $start = 0;
7715  }
7716  if (strlen($end) == 0) {
7717  $end = count($strarr);
7718  }
7719  $string = '';
7720  for ($i=$start; $i < $end; ++$i) {
7721  $string .= $this->unichr($strarr[$i]);
7722  }
7723  return $string;
7724  }
7725 
7735  public function UniArrSubString($uniarr, $start='', $end='') {
7736  if (strlen($start) == 0) {
7737  $start = 0;
7738  }
7739  if (strlen($end) == 0) {
7740  $end = count($uniarr);
7741  }
7742  $string = '';
7743  for ($i=$start; $i < $end; ++$i) {
7744  $string .= $uniarr[$i];
7745  }
7746  return $string;
7747  }
7748 
7756  public function UTF8ArrayToUniArray($ta) {
7757  return array_map(array($this, 'unichr'), $ta);
7758  }
7759 
7768  public function unichr($c) {
7769  if (!$this->isunicode) {
7770  return chr($c);
7771  } elseif ($c <= 0x7F) {
7772  // one byte
7773  return chr($c);
7774  } elseif ($c <= 0x7FF) {
7775  // two bytes
7776  return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F);
7777  } elseif ($c <= 0xFFFF) {
7778  // three bytes
7779  return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
7780  } elseif ($c <= 0x10FFFF) {
7781  // four bytes
7782  return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
7783  } else {
7784  return '';
7785  }
7786  }
7787 
7795  public function getImageFileType($imgfile, $iminfo=array()) {
7796  $type = '';
7797  if (isset($iminfo['mime']) AND !empty($iminfo['mime'])) {
7798  $mime = explode('/', $iminfo['mime']);
7799  if ((count($mime) > 1) AND ($mime[0] == 'image') AND (!empty($mime[1]))) {
7800  $type = strtolower(trim($mime[1]));
7801  }
7802  }
7803  if (empty($type)) {
7804  $fileinfo = pathinfo($imgfile);
7805  if (isset($fileinfo['extension']) AND (!$this->empty_string($fileinfo['extension']))) {
7806  $type = strtolower(trim($fileinfo['extension']));
7807  }
7808  }
7809  if ($type == 'jpg') {
7810  $type = 'jpeg';
7811  }
7812  return $type;
7813  }
7814 
7826  protected function fitBlock($w, $h, $x, $y, $fitonpage=false) {
7827  if ($w <= 0) {
7828  // set maximum width
7829  $w = ($this->w - $this->lMargin - $this->rMargin);
7830  }
7831  if ($h <= 0) {
7832  // set maximum height
7833  $h = ($this->PageBreakTrigger - $this->tMargin);
7834  }
7835  // resize the block to be vertically contained on a single page or single column
7836  if ($fitonpage OR $this->AutoPageBreak) {
7837  $ratio_wh = ($w / $h);
7838  if ($h > ($this->PageBreakTrigger - $this->tMargin)) {
7839  $h = $this->PageBreakTrigger - $this->tMargin;
7840  $w = ($h * $ratio_wh);
7841  }
7842  // resize the block to be horizontally contained on a single page or single column
7843  if ($fitonpage) {
7844  $maxw = ($this->w - $this->lMargin - $this->rMargin);
7845  if ($w > $maxw) {
7846  $w = $maxw;
7847  $h = ($w / $ratio_wh);
7848  }
7849  }
7850  }
7851  // Check whether we need a new page or new column first as this does not fit
7852  $prev_x = $this->x;
7853  $prev_y = $this->y;
7854  if ($this->checkPageBreak($h, $y) OR ($this->y < $prev_y)) {
7855  $y = $this->y;
7856  if ($this->rtl) {
7857  $x += ($prev_x - $this->x);
7858  } else {
7859  $x += ($this->x - $prev_x);
7860  }
7861  $this->newline = true;
7862  }
7863  // resize the block to be contained on the remaining available page or column space
7864  if ($fitonpage) {
7865  $ratio_wh = ($w / $h);
7866  if (($y + $h) > $this->PageBreakTrigger) {
7867  $h = $this->PageBreakTrigger - $y;
7868  $w = ($h * $ratio_wh);
7869  }
7870  if ((!$this->rtl) AND (($x + $w) > ($this->w - $this->rMargin))) {
7871  $w = $this->w - $this->rMargin - $x;
7872  $h = ($w / $ratio_wh);
7873  } elseif (($this->rtl) AND (($x - $w) < ($this->lMargin))) {
7874  $w = $x - $this->lMargin;
7875  $h = ($w / $ratio_wh);
7876  }
7877  }
7878  return array($w, $h, $x, $y);
7879  }
7880 
7915  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()) {
7916  if ($this->state != 2) {
7917  return;
7918  }
7919  if ($x === '') {
7920  $x = $this->x;
7921  }
7922  if ($y === '') {
7923  $y = $this->y;
7924  }
7925  // check page for no-write regions and adapt page margins if necessary
7926  list($x, $y) = $this->checkPageRegions($h, $x, $y);
7927  $exurl = ''; // external streams
7928  // check if we are passing an image as file or string
7929  if ($file[0] === '@') {
7930  // image from string
7931  $imgdata = substr($file, 1);
7932  $file = $this->getObjFilename('img');
7933  $fp = fopen($file, 'w');
7934  fwrite($fp, $imgdata);
7935  fclose($fp);
7936  unset($imgdata);
7937  $imsize = @getimagesize($file);
7938  if ($imsize === FALSE) {
7939  unlink($file);
7940  } else {
7941  $this->cached_files[] = $file;
7942  }
7943  } else { // image file
7944  if ($file{0} === '*') {
7945  // image as external stream
7946  $file = substr($file, 1);
7947  $exurl = $file;
7948  }
7949  // check if is local file
7950  if (!@file_exists($file)) {
7951  // encode spaces on filename (file is probably an URL)
7952  $file = str_replace(' ', '%20', $file);
7953  }
7954  if (@file_exists($file)) {
7955  // get image dimensions
7956  $imsize = @getimagesize($file);
7957  } else {
7958  $imsize = false;
7959  }
7960  if ($imsize === FALSE) {
7961  if (function_exists('curl_init')) {
7962  // try to get remote file data using cURL
7963  $cs = curl_init(); // curl session
7964  curl_setopt($cs, CURLOPT_URL, $file);
7965  curl_setopt($cs, CURLOPT_BINARYTRANSFER, true);
7966  curl_setopt($cs, CURLOPT_FAILONERROR, true);
7967  curl_setopt($cs, CURLOPT_RETURNTRANSFER, true);
7968  if ((ini_get('open_basedir') == '') AND (!ini_get('safe_mode'))) {
7969  curl_setopt($cs, CURLOPT_FOLLOWLOCATION, true);
7970  }
7971  curl_setopt($cs, CURLOPT_CONNECTTIMEOUT, 5);
7972  curl_setopt($cs, CURLOPT_TIMEOUT, 30);
7973  curl_setopt($cs, CURLOPT_SSL_VERIFYPEER, false);
7974  curl_setopt($cs, CURLOPT_SSL_VERIFYHOST, false);
7975  curl_setopt($cs, CURLOPT_USERAGENT, 'TCPDF');
7976  $imgdata = curl_exec($cs);
7977  curl_close($cs);
7978  if ($imgdata !== FALSE) {
7979  // copy image to cache
7980  $file = $this->getObjFilename('img');
7981  $fp = fopen($file, 'w');
7982  fwrite($fp, $imgdata);
7983  fclose($fp);
7984  unset($imgdata);
7985  $imsize = @getimagesize($file);
7986  if ($imsize === FALSE) {
7987  unlink($file);
7988  } else {
7989  $this->cached_files[] = $file;
7990  }
7991  }
7992  } elseif (($w > 0) AND ($h > 0)) {
7993  // get measures from specified data
7994  $pw = $this->getHTMLUnitToUnits($w, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
7995  $ph = $this->getHTMLUnitToUnits($h, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
7996  $imsize = array($pw, $ph);
7997  }
7998  }
7999  }
8000  if ($imsize === FALSE) {
8001  if (substr($file, 0, -34) == K_PATH_CACHE.'msk') { // mask file
8002  // get measures from specified data
8003  $pw = $this->getHTMLUnitToUnits($w, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
8004  $ph = $this->getHTMLUnitToUnits($h, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
8005  $imsize = array($pw, $ph);
8006  } else {
8007  $this->Error('[Image] Unable to get image: '.$file);
8008  }
8009  }
8010  // file hash
8011  $filehash = md5($this->file_id.$file);
8012  // get original image width and height in pixels
8013  list($pixw, $pixh) = $imsize;
8014  // calculate image width and height on document
8015  if (($w <= 0) AND ($h <= 0)) {
8016  // convert image size to document unit
8017  $w = $this->pixelsToUnits($pixw);
8018  $h = $this->pixelsToUnits($pixh);
8019  } elseif ($w <= 0) {
8020  $w = $h * $pixw / $pixh;
8021  } elseif ($h <= 0) {
8022  $h = $w * $pixh / $pixw;
8023  } elseif (($fitbox !== false) AND ($w > 0) AND ($h > 0)) {
8024  if (strlen($fitbox) !== 2) {
8025  // set default alignment
8026  $fitbox = '--';
8027  }
8028  // scale image dimensions proportionally to fit within the ($w, $h) box
8029  if ((($w * $pixh) / ($h * $pixw)) < 1) {
8030  // store current height
8031  $oldh = $h;
8032  // calculate new height
8033  $h = $w * $pixh / $pixw;
8034  // height difference
8035  $hdiff = ($oldh - $h);
8036  // vertical alignment
8037  switch (strtoupper($fitbox{1})) {
8038  case 'T': {
8039  break;
8040  }
8041  case 'M': {
8042  $y += ($hdiff / 2);
8043  break;
8044  }
8045  case 'B': {
8046  $y += $hdiff;
8047  break;
8048  }
8049  }
8050  } else {
8051  // store current width
8052  $oldw = $w;
8053  // calculate new width
8054  $w = $h * $pixw / $pixh;
8055  // width difference
8056  $wdiff = ($oldw - $w);
8057  // horizontal alignment
8058  switch (strtoupper($fitbox{0})) {
8059  case 'L': {
8060  if ($this->rtl) {
8061  $x -= $wdiff;
8062  }
8063  break;
8064  }
8065  case 'C': {
8066  if ($this->rtl) {
8067  $x -= ($wdiff / 2);
8068  } else {
8069  $x += ($wdiff / 2);
8070  }
8071  break;
8072  }
8073  case 'R': {
8074  if (!$this->rtl) {
8075  $x += $wdiff;
8076  }
8077  break;
8078  }
8079  }
8080  }
8081  }
8082  // fit the image on available space
8083  list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
8084  // calculate new minimum dimensions in pixels
8085  $neww = round($w * $this->k * $dpi / $this->dpi);
8086  $newh = round($h * $this->k * $dpi / $this->dpi);
8087  // check if resize is necessary (resize is used only to reduce the image)
8088  $newsize = ($neww * $newh);
8089  $pixsize = ($pixw * $pixh);
8090  if (intval($resize) == 2) {
8091  $resize = true;
8092  } elseif ($newsize >= $pixsize) {
8093  $resize = false;
8094  }
8095  // check if image has been already added on document
8096  $newimage = true;
8097  if (in_array($file, $this->imagekeys)) {
8098  $newimage = false;
8099  // get existing image data
8100  $info = $this->getImageBuffer($file);
8101  if (substr($file, 0, -34) != K_PATH_CACHE.'msk') {
8102  // check if the newer image is larger
8103  $oldsize = ($info['w'] * $info['h']);
8104  if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
8105  $newimage = true;
8106  }
8107  }
8108  } elseif (substr($file, 0, -34) != K_PATH_CACHE.'msk') {
8109  // check for cached images with alpha channel
8110  $tempfile_plain = K_PATH_CACHE.'mskp_'.$filehash;
8111  $tempfile_alpha = K_PATH_CACHE.'mska_'.$filehash;
8112  if (in_array($tempfile_plain, $this->imagekeys)) {
8113  // get existing image data
8114  $info = $this->getImageBuffer($tempfile_plain);
8115  // check if the newer image is larger
8116  $oldsize = ($info['w'] * $info['h']);
8117  if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
8118  $newimage = true;
8119  } else {
8120  $newimage = false;
8121  // embed mask image
8122  $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
8123  // embed image, masked with previously embedded mask
8124  return $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
8125  }
8126  }
8127  }
8128  if ($newimage) {
8129  //First use of image, get info
8130  $type = strtolower($type);
8131  if ($type == '') {
8132  $type = $this->getImageFileType($file, $imsize);
8133  } elseif ($type == 'jpg') {
8134  $type = 'jpeg';
8135  }
8136  $mqr = $this->get_mqr();
8137  $this->set_mqr(false);
8138  // Specific image handlers
8139  $mtd = '_parse'.$type;
8140  // GD image handler function
8141  $gdfunction = 'imagecreatefrom'.$type;
8142  $info = false;
8143  if ((method_exists($this, $mtd)) AND (!($resize AND (function_exists($gdfunction) OR extension_loaded('imagick'))))) {
8144  // TCPDF image functions
8145  $info = $this->$mtd($file);
8146  if ($info == 'pngalpha') {
8147  return $this->ImagePngAlpha($file, $x, $y, $pixw, $pixh, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign, $filehash);
8148  }
8149  }
8150  if (!$info) {
8151  if (function_exists($gdfunction)) {
8152  // GD library
8153  $img = $gdfunction($file);
8154  if ($resize) {
8155  $imgr = imagecreatetruecolor($neww, $newh);
8156  if (($type == 'gif') OR ($type == 'png')) {
8157  $imgr = $this->_setGDImageTransparency($imgr, $img);
8158  }
8159  imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh);
8160  if (($type == 'gif') OR ($type == 'png')) {
8161  $info = $this->_toPNG($imgr);
8162  } else {
8163  $info = $this->_toJPEG($imgr);
8164  }
8165  } else {
8166  if (($type == 'gif') OR ($type == 'png')) {
8167  $info = $this->_toPNG($img);
8168  } else {
8169  $info = $this->_toJPEG($img);
8170  }
8171  }
8172  } elseif (extension_loaded('imagick')) {
8173  // ImageMagick library
8174  $img = new Imagick();
8175  if ($type == 'SVG') {
8176  // get SVG file content
8177  $svgimg = file_get_contents($file);
8178  // get width and height
8179  $regs = array();
8180  if (preg_match('/<svg([^>]*)>/si', $svgimg, $regs)) {
8181  $svgtag = $regs[1];
8182  $tmp = array();
8183  if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
8184  $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
8185  $owu = sprintf('%F', ($ow * $dpi / 72)).$this->pdfunit;
8186  $svgtag = preg_replace('/[\s]+width[\s]*=[\s]*"[^"]*"/si', ' width="'.$owu.'"', $svgtag, 1);
8187  } else {
8188  $ow = $w;
8189  }
8190  $tmp = array();
8191  if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
8192  $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
8193  $ohu = sprintf('%F', ($oh * $dpi / 72)).$this->pdfunit;
8194  $svgtag = preg_replace('/[\s]+height[\s]*=[\s]*"[^"]*"/si', ' height="'.$ohu.'"', $svgtag, 1);
8195  } else {
8196  $oh = $h;
8197  }
8198  $tmp = array();
8199  if (!preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $svgtag, $tmp)) {
8200  $vbw = ($ow * $this->imgscale * $this->k);
8201  $vbh = ($oh * $this->imgscale * $this->k);
8202  $vbox = sprintf(' viewBox="0 0 %F %F" ', $vbw, $vbh);
8203  $svgtag = $vbox.$svgtag;
8204  }
8205  $svgimg = preg_replace('/<svg([^>]*)>/si', '<svg'.$svgtag.'>', $svgimg, 1);
8206  }
8207  $img->readImageBlob($svgimg);
8208  } else {
8209  $img->readImage($file);
8210  }
8211  if ($resize) {
8212  $img->resizeImage($neww, $newh, 10, 1, false);
8213  }
8214  $img->setCompressionQuality($this->jpeg_quality);
8215  $img->setImageFormat('jpeg');
8216  $tempname = tempnam(K_PATH_CACHE, 'jpg_');
8217  $img->writeImage($tempname);
8218  $info = $this->_parsejpeg($tempname);
8219  unlink($tempname);
8220  $img->destroy();
8221  } else {
8222  return;
8223  }
8224  }
8225  if ($info === false) {
8226  //If false, we cannot process image
8227  return;
8228  }
8229  $this->set_mqr($mqr);
8230  if ($ismask) {
8231  // force grayscale
8232  $info['cs'] = 'DeviceGray';
8233  }
8234  if ($imgmask !== false) {
8235  $info['masked'] = $imgmask;
8236  }
8237  if (!empty($exurl)) {
8238  $info['exurl'] = $exurl;
8239  }
8240  // array of alternative images
8241  $info['altimgs'] = $altimgs;
8242  // add image to document
8243  $info['i'] = $this->setImageBuffer($file, $info);
8244  }
8245  // set alignment
8246  $this->img_rb_y = $y + $h;
8247  // set alignment
8248  if ($this->rtl) {
8249  if ($palign == 'L') {
8250  $ximg = $this->lMargin;
8251  } elseif ($palign == 'C') {
8252  $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
8253  } elseif ($palign == 'R') {
8254  $ximg = $this->w - $this->rMargin - $w;
8255  } else {
8256  $ximg = $x - $w;
8257  }
8258  $this->img_rb_x = $ximg;
8259  } else {
8260  if ($palign == 'L') {
8261  $ximg = $this->lMargin;
8262  } elseif ($palign == 'C') {
8263  $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
8264  } elseif ($palign == 'R') {
8265  $ximg = $this->w - $this->rMargin - $w;
8266  } else {
8267  $ximg = $x;
8268  }
8269  $this->img_rb_x = $ximg + $w;
8270  }
8271  if ($ismask OR $hidden) {
8272  // image is not displayed
8273  return $info['i'];
8274  }
8275  $xkimg = $ximg * $this->k;
8276  if (!$alt) {
8277  // only non-alternative immages will be set
8278  $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']));
8279  }
8280  if (!empty($border)) {
8281  $bx = $this->x;
8282  $by = $this->y;
8283  $this->x = $ximg;
8284  if ($this->rtl) {
8285  $this->x += $w;
8286  }
8287  $this->y = $y;
8288  $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
8289  $this->x = $bx;
8290  $this->y = $by;
8291  }
8292  if ($link) {
8293  $this->Link($ximg, $y, $w, $h, $link, 0);
8294  }
8295  // set pointer to align the next text/objects
8296  switch($align) {
8297  case 'T': {
8298  $this->y = $y;
8299  $this->x = $this->img_rb_x;
8300  break;
8301  }
8302  case 'M': {
8303  $this->y = $y + round($h/2);
8304  $this->x = $this->img_rb_x;
8305  break;
8306  }
8307  case 'B': {
8308  $this->y = $this->img_rb_y;
8309  $this->x = $this->img_rb_x;
8310  break;
8311  }
8312  case 'N': {
8313  $this->SetY($this->img_rb_y);
8314  break;
8315  }
8316  default:{
8317  break;
8318  }
8319  }
8320  $this->endlinex = $this->img_rb_x;
8321  if ($this->inxobj) {
8322  // we are inside an XObject template
8323  $this->xobjects[$this->xobjid]['images'][] = $info['i'];
8324  }
8325  return $info['i'];
8326  }
8327 
8333  public function set_mqr($mqr) {
8334  if (!defined('PHP_VERSION_ID')) {
8335  $version = PHP_VERSION;
8336  define('PHP_VERSION_ID', (($version{0} * 10000) + ($version{2} * 100) + $version{4}));
8337  }
8338  if (PHP_VERSION_ID < 50300) {
8339  @set_magic_quotes_runtime($mqr);
8340  }
8341  }
8342 
8348  public function get_mqr() {
8349  if (!defined('PHP_VERSION_ID')) {
8350  $version = PHP_VERSION;
8351  define('PHP_VERSION_ID', (($version{0} * 10000) + ($version{2} * 100) + $version{4}));
8352  }
8353  if (PHP_VERSION_ID < 50300) {
8354  return @get_magic_quotes_runtime();
8355  }
8356  return 0;
8357  }
8358 
8366  protected function _toJPEG($image) {
8367  $tempname = tempnam(K_PATH_CACHE, 'jpg_');
8368  imagejpeg($image, $tempname, $this->jpeg_quality);
8369  imagedestroy($image);
8370  $retvars = $this->_parsejpeg($tempname);
8371  // tidy up by removing temporary image
8372  unlink($tempname);
8373  return $retvars;
8374  }
8375 
8384  protected function _toPNG($image) {
8385  // set temporary image file name
8386  $tempname = tempnam(K_PATH_CACHE, 'jpg_');
8387  // turn off interlaced mode
8388  imageinterlace($image, 0);
8389  // create temporary PNG image
8390  imagepng($image, $tempname);
8391  // remove image from memory
8392  imagedestroy($image);
8393  // get PNG image data
8394  $retvars = $this->_parsepng($tempname);
8395  // tidy up by removing temporary image
8396  unlink($tempname);
8397  return $retvars;
8398  }
8399 
8408  protected function _setGDImageTransparency($new_image, $image) {
8409  // transparency index
8410  $tid = imagecolortransparent($image);
8411  // default transparency color
8412  $tcol = array('red' => 255, 'green' => 255, 'blue' => 255);
8413  if ($tid >= 0) {
8414  // get the colors for the transparency index
8415  $tcol = imagecolorsforindex($image, $tid);
8416  }
8417  $tid = imagecolorallocate($new_image, $tcol['red'], $tcol['green'], $tcol['blue']);
8418  imagefill($new_image, 0, 0, $tid);
8419  imagecolortransparent($new_image, $tid);
8420  return $new_image;
8421  }
8422 
8429  protected function _parsejpeg($file) {
8430  $a = getimagesize($file);
8431  if (empty($a)) {
8432  $this->Error('Missing or incorrect image file: '.$file);
8433  }
8434  if ($a[2] != 2) {
8435  $this->Error('Not a JPEG file: '.$file);
8436  }
8437  // bits per pixel
8438  $bpc = isset($a['bits']) ? intval($a['bits']) : 8;
8439  // number of image channels
8440  if (!isset($a['channels'])) {
8441  $channels = 3;
8442  } else {
8443  $channels = intval($a['channels']);
8444  }
8445  // default colour space
8446  switch ($channels) {
8447  case 1: {
8448  $colspace = 'DeviceGray';
8449  break;
8450  }
8451  case 3: {
8452  $colspace = 'DeviceRGB';
8453  break;
8454  }
8455  case 4: {
8456  $colspace = 'DeviceCMYK';
8457  break;
8458  }
8459  default: {
8460  $channels = 3;
8461  $colspace = 'DeviceRGB';
8462  break;
8463  }
8464  }
8465  // get file content
8466  $data = file_get_contents($file);
8467  // check for embedded ICC profile
8468  $icc = array();
8469  $offset = 0;
8470  while (($pos = strpos($data, "ICC_PROFILE\0", $offset)) !== false) {
8471  // get ICC sequence length
8472  $length = ($this->_getUSHORT($data, ($pos - 2)) - 16);
8473  // marker sequence number
8474  $msn = max(1, ord($data[($pos + 12)]));
8475  // number of markers (total of APP2 used)
8476  $nom = max(1, ord($data[($pos + 13)]));
8477  // get sequence segment
8478  $icc[($msn - 1)] = substr($data, ($pos + 14), $length);
8479  // move forward to next sequence
8480  $offset = ($pos + 14 + $length);
8481  }
8482  // order and compact ICC segments
8483  if (count($icc) > 0) {
8484  ksort($icc);
8485  $icc = implode('', $icc);
8486  if ((ord($icc{36}) != 0x61) OR (ord($icc{37}) != 0x63) OR (ord($icc{38}) != 0x73) OR (ord($icc{39}) != 0x70)) {
8487  // invalid ICC profile
8488  $icc = false;
8489  }
8490  } else {
8491  $icc = false;
8492  }
8493  return array('w' => $a[0], 'h' => $a[1], 'ch' => $channels, 'icc' => $icc, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data);
8494  }
8495 
8502  protected function _parsepng($file) {
8503  $f = fopen($file, 'rb');
8504  if ($f === false) {
8505  $this->Error('Can\'t open image file: '.$file);
8506  }
8507  //Check signature
8508  if (fread($f, 8) != chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) {
8509  $this->Error('Not a PNG file: '.$file);
8510  }
8511  //Read header chunk
8512  fread($f, 4);
8513  if (fread($f, 4) != 'IHDR') {
8514  $this->Error('Incorrect PNG file: '.$file);
8515  }
8516  $w = $this->_freadint($f);
8517  $h = $this->_freadint($f);
8518  $bpc = ord(fread($f, 1));
8519  if ($bpc > 8) {
8520  //$this->Error('16-bit depth not supported: '.$file);
8521  fclose($f);
8522  return false;
8523  }
8524  $ct = ord(fread($f, 1));
8525  if ($ct == 0) {
8526  $colspace = 'DeviceGray';
8527  } elseif ($ct == 2) {
8528  $colspace = 'DeviceRGB';
8529  } elseif ($ct == 3) {
8530  $colspace = 'Indexed';
8531  } else {
8532  // alpha channel
8533  fclose($f);
8534  return 'pngalpha';
8535  }
8536  if (ord(fread($f, 1)) != 0) {
8537  //$this->Error('Unknown compression method: '.$file);
8538  fclose($f);
8539  return false;
8540  }
8541  if (ord(fread($f, 1)) != 0) {
8542  //$this->Error('Unknown filter method: '.$file);
8543  fclose($f);
8544  return false;
8545  }
8546  if (ord(fread($f, 1)) != 0) {
8547  //$this->Error('Interlacing not supported: '.$file);
8548  fclose($f);
8549  return false;
8550  }
8551  fread($f, 4);
8552  $channels = ($ct == 2 ? 3 : 1);
8553  $parms = '/DecodeParms << /Predictor 15 /Colors '.$channels.' /BitsPerComponent '.$bpc.' /Columns '.$w.' >>';
8554  //Scan chunks looking for palette, transparency and image data
8555  $pal = '';
8556  $trns = '';
8557  $data = '';
8558  $icc = false;
8559  do {
8560  $n = $this->_freadint($f);
8561  $type = fread($f, 4);
8562  if ($type == 'PLTE') {
8563  // read palette
8564  $pal = $this->rfread($f, $n);
8565  fread($f, 4);
8566  } elseif ($type == 'tRNS') {
8567  // read transparency info
8568  $t = $this->rfread($f, $n);
8569  if ($ct == 0) {
8570  $trns = array(ord($t{1}));
8571  } elseif ($ct == 2) {
8572  $trns = array(ord($t{1}), ord($t{3}), ord($t{5}));
8573  } else {
8574  $pos = strpos($t, chr(0));
8575  if ($pos !== false) {
8576  $trns = array($pos);
8577  }
8578  }
8579  fread($f, 4);
8580  } elseif ($type == 'IDAT') {
8581  // read image data block
8582  $data .= $this->rfread($f, $n);
8583  fread($f, 4);
8584  } elseif ($type == 'iCCP') {
8585  // skip profile name
8586  $len = 0;
8587  while ((ord(fread($f, 1)) > 0) AND ($len < 80)) {
8588  ++$len;
8589  }
8590  // skip null separator
8591  fread($f, 1);
8592  // get compression method
8593  if (ord(fread($f, 1)) != 0) {
8594  //$this->Error('Unknown filter method: '.$file);
8595  fclose($f);
8596  return false;
8597  }
8598  // read ICC Color Profile
8599  $icc = $this->rfread($f, ($n - $len - 2));
8600  // decompress profile
8601  $icc = gzuncompress($icc);
8602  fread($f, 4);
8603  } elseif ($type == 'IEND') {
8604  break;
8605  } else {
8606  $this->rfread($f, $n + 4);
8607  }
8608  } while ($n);
8609  if (($colspace == 'Indexed') AND (empty($pal))) {
8610  //$this->Error('Missing palette in '.$file);
8611  fclose($f);
8612  return false;
8613  }
8614  fclose($f);
8615  return array('w' => $w, 'h' => $h, 'ch' => $channels, 'icc' => $icc, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'parms' => $parms, 'pal' => $pal, 'trns' => $trns, 'data' => $data);
8616  }
8617 
8628  protected function rfread($handle, $length) {
8629  $data = fread($handle, $length);
8630  if ($data === false) {
8631  return false;
8632  }
8633  $rest = $length - strlen($data);
8634  if ($rest > 0) {
8635  $data .= $this->rfread($handle, $rest);
8636  }
8637  return $data;
8638  }
8639 
8661  protected function ImagePngAlpha($file, $x, $y, $wpx, $hpx, $w, $h, $type, $link, $align, $resize, $dpi, $palign, $filehash='') {
8662  if (empty($filehash)) {
8663  $filehash = md5($this->file_id.$file);
8664  }
8665  // create temp image file (without alpha channel)
8666  $tempfile_plain = K_PATH_CACHE.'mskp_'.$filehash;
8667  // create temp alpha file
8668  $tempfile_alpha = K_PATH_CACHE.'mska_'.$filehash;
8669  if (extension_loaded('imagick')) { // ImageMagick extension
8670  // ImageMagick library
8671  $img = new Imagick();
8672  $img->readImage($file);
8673  // clone image object
8674  $imga = $this->objclone($img);
8675  // extract alpha channel
8676  $img->separateImageChannel(8); // 8 = (imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE);
8677  $img->negateImage(true);
8678  $img->setImageFormat('png');
8679  $img->writeImage($tempfile_alpha);
8680  // remove alpha channel
8681  $imga->separateImageChannel(39); // 39 = (imagick::CHANNEL_ALL & ~(imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE));
8682  $imga->setImageFormat('png');
8683  $imga->writeImage($tempfile_plain);
8684  } elseif (function_exists('imagecreatefrompng')) { // GD extension
8685  // generate images
8686  $img = imagecreatefrompng($file);
8687  $imgalpha = imagecreate($wpx, $hpx);
8688  // generate gray scale palette (0 -> 255)
8689  for ($c = 0; $c < 256; ++$c) {
8690  ImageColorAllocate($imgalpha, $c, $c, $c);
8691  }
8692  // extract alpha channel
8693  for ($xpx = 0; $xpx < $wpx; ++$xpx) {
8694  for ($ypx = 0; $ypx < $hpx; ++$ypx) {
8695  $color = imagecolorat($img, $xpx, $ypx);
8696  $alpha = $this->getGDgamma($color); // correct gamma
8697  imagesetpixel($imgalpha, $xpx, $ypx, $alpha);
8698  }
8699  }
8700  imagepng($imgalpha, $tempfile_alpha);
8701  imagedestroy($imgalpha);
8702  // extract image without alpha channel
8703  $imgplain = imagecreatetruecolor($wpx, $hpx);
8704  imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
8705  imagepng($imgplain, $tempfile_plain);
8706  imagedestroy($imgplain);
8707  } else {
8708  $this->Error('TCPDF requires the Imagick or GD extension to handle PNG images with alpha channel.');
8709  }
8710  // embed mask image
8711  $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
8712  // embed image, masked with previously embedded mask
8713  $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
8714  // remove temp files
8715  unlink($tempfile_alpha);
8716  unlink($tempfile_plain);
8717  }
8718 
8725  protected function getGDgamma($c) {
8726  if (!isset($this->gdgammacache["'".$c."'"])) {
8727  // shifts off the first 24 bits (where 8x3 are used for each color),
8728  // and returns the remaining 7 allocated bits (commonly used for alpha)
8729  $alpha = ($c >> 24);
8730  // GD alpha is only 7 bit (0 -> 127)
8731  $alpha = (((127 - $alpha) / 127) * 255);
8732  // correct gamma
8733  $this->gdgammacache["'".$c."'"] = (pow(($alpha / 255), 2.2) * 255);
8734  // store the latest values on cache to improve performances
8735  if (count($this->gdgammacache) > 8) {
8736  // remove one element from the cache array
8737  array_shift($this->gdgammacache);
8738  }
8739  }
8740  return $this->gdgammacache["'".$c."'"];
8741  }
8742 
8752  public function Ln($h='', $cell=false) {
8753  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'])) {
8754  // revove vertical space from the top of the column
8755  return;
8756  }
8757  if ($cell) {
8758  if ($this->rtl) {
8759  $cellpadding = $this->cell_padding['R'];
8760  } else {
8761  $cellpadding = $this->cell_padding['L'];
8762  }
8763  } else {
8764  $cellpadding = 0;
8765  }
8766  if ($this->rtl) {
8767  $this->x = $this->w - $this->rMargin - $cellpadding;
8768  } else {
8769  $this->x = $this->lMargin + $cellpadding;
8770  }
8771  if (is_string($h)) {
8772  $this->y += $this->lasth;
8773  } else {
8774  $this->y += $h;
8775  }
8776  $this->newline = true;
8777  }
8778 
8787  public function GetX() {
8788  //Get x position
8789  if ($this->rtl) {
8790  return ($this->w - $this->x);
8791  } else {
8792  return $this->x;
8793  }
8794  }
8795 
8803  public function GetAbsX() {
8804  return $this->x;
8805  }
8806 
8814  public function GetY() {
8815  return $this->y;
8816  }
8817 
8827  public function SetX($x, $rtloff=false) {
8828  $x = floatval($x);
8829  if (!$rtloff AND $this->rtl) {
8830  if ($x >= 0) {
8831  $this->x = $this->w - $x;
8832  } else {
8833  $this->x = abs($x);
8834  }
8835  } else {
8836  if ($x >= 0) {
8837  $this->x = $x;
8838  } else {
8839  $this->x = $this->w + $x;
8840  }
8841  }
8842  if ($this->x < 0) {
8843  $this->x = 0;
8844  }
8845  if ($this->x > $this->w) {
8846  $this->x = $this->w;
8847  }
8848  }
8849 
8860  public function SetY($y, $resetx=true, $rtloff=false) {
8861  $y = floatval($y);
8862  if ($resetx) {
8863  //reset x
8864  if (!$rtloff AND $this->rtl) {
8865  $this->x = $this->w - $this->rMargin;
8866  } else {
8867  $this->x = $this->lMargin;
8868  }
8869  }
8870  if ($y >= 0) {
8871  $this->y = $y;
8872  } else {
8873  $this->y = $this->h + $y;
8874  }
8875  if ($this->y < 0) {
8876  $this->y = 0;
8877  }
8878  if ($this->y > $this->h) {
8879  $this->y = $this->h;
8880  }
8881  }
8882 
8893  public function SetXY($x, $y, $rtloff=false) {
8894  $this->SetY($y, false, $rtloff);
8895  $this->SetX($x, $rtloff);
8896  }
8897 
8905  public function SetAbsX($x) {
8906  $this->x = floatval($x);
8907  }
8908 
8916  public function SetAbsY($y) {
8917  $this->y = floatval($y);
8918  }
8919 
8928  public function SetAbsXY($x, $y) {
8929  $this->SetAbsX($x);
8930  $this->SetAbsY($y);
8931  }
8932 
8940  protected function sendOutputData($data, $length) {
8941  if (!isset($_SERVER['HTTP_ACCEPT_ENCODING']) OR empty($_SERVER['HTTP_ACCEPT_ENCODING'])) {
8942  // the content length may vary if the server is using compression
8943  header('Content-Length: '.$length);
8944  }
8945  echo $data;
8946  }
8947 
8958  public function Output($name='doc.pdf', $dest='I') {
8959  //Output PDF to some destination
8960  //Finish document if necessary
8961  if ($this->state < 3) {
8962  $this->Close();
8963  }
8964  //Normalize parameters
8965  if (is_bool($dest)) {
8966  $dest = $dest ? 'D' : 'F';
8967  }
8968  $dest = strtoupper($dest);
8969  if ($dest{0} != 'F') {
8970  $name = preg_replace('/[\s]+/', '_', $name);
8971  $name = preg_replace('/[^a-zA-Z0-9_\.-]/', '', $name);
8972  }
8973  if ($this->sign) {
8974  // *** apply digital signature to the document ***
8975  // get the document content
8976  $pdfdoc = $this->getBuffer();
8977  // remove last newline
8978  $pdfdoc = substr($pdfdoc, 0, -1);
8979  // Remove the original buffer
8980  if (isset($this->diskcache) AND $this->diskcache) {
8981  // remove buffer file from cache
8982  unlink($this->buffer);
8983  }
8984  unset($this->buffer);
8985  // remove filler space
8986  $byterange_string_len = strlen($this->byterange_string);
8987  // define the ByteRange
8988  $byte_range = array();
8989  $byte_range[0] = 0;
8990  $byte_range[1] = strpos($pdfdoc, $this->byterange_string) + $byterange_string_len + 10;
8991  $byte_range[2] = $byte_range[1] + $this->signature_max_length + 2;
8992  $byte_range[3] = strlen($pdfdoc) - $byte_range[2];
8993  $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).substr($pdfdoc, $byte_range[2]);
8994  // replace the ByteRange
8995  $byterange = sprintf('/ByteRange[0 %u %u %u]', $byte_range[1], $byte_range[2], $byte_range[3]);
8996  $byterange .= str_repeat(' ', ($byterange_string_len - strlen($byterange)));
8997  $pdfdoc = str_replace($this->byterange_string, $byterange, $pdfdoc);
8998  // write the document to a temporary folder
8999  $tempdoc = tempnam(K_PATH_CACHE, 'tmppdf_');
9000  $f = fopen($tempdoc, 'wb');
9001  if (!$f) {
9002  $this->Error('Unable to create temporary file: '.$tempdoc);
9003  }
9004  $pdfdoc_length = strlen($pdfdoc);
9005  fwrite($f, $pdfdoc, $pdfdoc_length);
9006  fclose($f);
9007  // get digital signature via openssl library
9008  $tempsign = tempnam(K_PATH_CACHE, 'tmpsig_');
9009  if (empty($this->signature_data['extracerts'])) {
9010  openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED);
9011  } else {
9012  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']);
9013  }
9014  unlink($tempdoc);
9015  // read signature
9016  $signature = file_get_contents($tempsign);
9017  unlink($tempsign);
9018  // extract signature
9019  $signature = substr($signature, $pdfdoc_length);
9020  $signature = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13));
9021  $tmparr = explode("\n\n", $signature);
9022  $signature = $tmparr[1];
9023  unset($tmparr);
9024  // decode signature
9025  $signature = base64_decode(trim($signature));
9026  // convert signature to hex
9027  $signature = current(unpack('H*', $signature));
9028  $signature = str_pad($signature, $this->signature_max_length, '0');
9029  // disable disk caching
9030  $this->diskcache = false;
9031  // Add signature to the document
9032  $this->buffer = substr($pdfdoc, 0, $byte_range[1]).'<'.$signature.'>'.substr($pdfdoc, $byte_range[1]);
9033  $this->bufferlen = strlen($this->buffer);
9034  }
9035  switch($dest) {
9036  case 'I': {
9037  // Send PDF to the standard output
9038  if (ob_get_contents()) {
9039  $this->Error('Some data has already been output, can\'t send PDF file');
9040  }
9041  if (php_sapi_name() != 'cli') {
9042  // send output to a browser
9043  header('Content-Type: application/pdf');
9044  if (headers_sent()) {
9045  $this->Error('Some data has already been output to browser, can\'t send PDF file');
9046  }
9047  header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
9048  //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
9049  header('Pragma: public');
9050  header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
9051  header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
9052  header('Content-Disposition: inline; filename="'.basename($name).'"');
9053  $this->sendOutputData($this->getBuffer(), $this->bufferlen);
9054  } else {
9055  echo $this->getBuffer();
9056  }
9057  break;
9058  }
9059  case 'D': {
9060  // download PDF as file
9061  if (ob_get_contents()) {
9062  $this->Error('Some data has already been output, can\'t send PDF file');
9063  }
9064  header('Content-Description: File Transfer');
9065  if (headers_sent()) {
9066  $this->Error('Some data has already been output to browser, can\'t send PDF file');
9067  }
9068  header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
9069  //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
9070  header('Pragma: public');
9071  header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
9072  header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
9073  // force download dialog
9074  if (strpos(php_sapi_name(), 'cgi') === false) {
9075  header('Content-Type: application/force-download');
9076  header('Content-Type: application/octet-stream', false);
9077  header('Content-Type: application/download', false);
9078  header('Content-Type: application/pdf', false);
9079  } else {
9080  header('Content-Type: application/pdf');
9081  }
9082  // use the Content-Disposition header to supply a recommended filename
9083  header('Content-Disposition: attachment; filename="'.basename($name).'"');
9084  header('Content-Transfer-Encoding: binary');
9085  $this->sendOutputData($this->getBuffer(), $this->bufferlen);
9086  break;
9087  }
9088  case 'F':
9089  case 'FI':
9090  case 'FD': {
9091  // save PDF to a local file
9092  if ($this->diskcache) {
9093  copy($this->buffer, $name);
9094  } else {
9095  $f = fopen($name, 'wb');
9096  if (!$f) {
9097  $this->Error('Unable to create output file: '.$name);
9098  }
9099  fwrite($f, $this->getBuffer(), $this->bufferlen);
9100  fclose($f);
9101  }
9102  if ($dest == 'FI') {
9103  // send headers to browser
9104  header('Content-Type: application/pdf');
9105  header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
9106  //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
9107  header('Pragma: public');
9108  header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
9109  header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
9110  header('Content-Disposition: inline; filename="'.basename($name).'"');
9111  $this->sendOutputData(file_get_contents($name), filesize($name));
9112  } elseif ($dest == 'FD') {
9113  // send headers to browser
9114  if (ob_get_contents()) {
9115  $this->Error('Some data has already been output, can\'t send PDF file');
9116  }
9117  header('Content-Description: File Transfer');
9118  if (headers_sent()) {
9119  $this->Error('Some data has already been output to browser, can\'t send PDF file');
9120  }
9121  header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
9122  header('Pragma: public');
9123  header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
9124  header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
9125  // force download dialog
9126  if (strpos(php_sapi_name(), 'cgi') === false) {
9127  header('Content-Type: application/force-download');
9128  header('Content-Type: application/octet-stream', false);
9129  header('Content-Type: application/download', false);
9130  header('Content-Type: application/pdf', false);
9131  } else {
9132  header('Content-Type: application/pdf');
9133  }
9134  // use the Content-Disposition header to supply a recommended filename
9135  header('Content-Disposition: attachment; filename="'.basename($name).'"');
9136  header('Content-Transfer-Encoding: binary');
9137  $this->sendOutputData(file_get_contents($name), filesize($name));
9138  }
9139  break;
9140  }
9141  case 'E': {
9142  // return PDF as base64 mime multi-part email attachment (RFC 2045)
9143  $retval = 'Content-Type: application/pdf;'."\r\n";
9144  $retval .= ' name="'.$name.'"'."\r\n";
9145  $retval .= 'Content-Transfer-Encoding: base64'."\r\n";
9146  $retval .= 'Content-Disposition: attachment;'."\r\n";
9147  $retval .= ' filename="'.$name.'"'."\r\n\r\n";
9148  $retval .= chunk_split(base64_encode($this->getBuffer()), 76, "\r\n");
9149  return $retval;
9150  }
9151  case 'S': {
9152  // returns PDF as a string
9153  return $this->getBuffer();
9154  }
9155  default: {
9156  $this->Error('Incorrect output destination: '.$dest);
9157  }
9158  }
9159  return '';
9160  }
9161 
9169  public function _destroy($destroyall=false, $preserve_objcopy=false) {
9170  if ($destroyall AND isset($this->diskcache) AND $this->diskcache AND (!$preserve_objcopy) AND (!$this->empty_string($this->buffer))) {
9171  // remove buffer file from cache
9172  unlink($this->buffer);
9173  }
9174  if ($destroyall AND isset($this->cached_files) AND !empty($this->cached_files)) {
9175  // remove cached files
9176  foreach ($this->cached_files as $cachefile) {
9177  if (is_file($cachefile)) {
9178  unlink($cachefile);
9179  }
9180  }
9181  unset($this->cached_files);
9182  }
9183  foreach (array_keys(get_object_vars($this)) as $val) {
9184  if ($destroyall OR (
9185  ($val != 'internal_encoding')
9186  AND ($val != 'state')
9187  AND ($val != 'bufferlen')
9188  AND ($val != 'buffer')
9189  AND ($val != 'diskcache')
9190  AND ($val != 'cached_files')
9191  AND ($val != 'sign')
9192  AND ($val != 'signature_data')
9193  AND ($val != 'signature_max_length')
9194  AND ($val != 'byterange_string')
9195  )) {
9196  if ((!$preserve_objcopy OR ($val != 'objcopy')) AND isset($this->$val)) {
9197  unset($this->$val);
9198  }
9199  }
9200  }
9201  }
9202 
9207  protected function _dochecks() {
9208  //Check for locale-related bug
9209  if (1.1 == 1) {
9210  $this->Error('Don\'t alter the locale before including class file');
9211  }
9212  //Check for decimal separator
9213  if (sprintf('%.1F', 1.0) != '1.0') {
9214  setlocale(LC_NUMERIC, 'C');
9215  }
9216  }
9217 
9223  protected function _getfontpath() {
9224  if (!defined('K_PATH_FONTS') AND is_dir(dirname(__FILE__).'/fonts')) {
9225  define('K_PATH_FONTS', dirname(__FILE__).'/fonts/');
9226  }
9227  return defined('K_PATH_FONTS') ? K_PATH_FONTS : '';
9228  }
9229 
9236  protected function getInternalPageNumberAliases($a= '') {
9237  $alias = array();
9238  // build array of Unicode + ASCII variants (the order is important)
9239  $alias = array('u' => array(), 'a' => array());
9240  $u = '{'.$a.'}';
9241  $alias['u'][] = $this->_escape($u);
9242  if ($this->isunicode) {
9243  $alias['u'][] = $this->_escape($this->UTF8ToLatin1($u));
9244  $alias['u'][] = $this->_escape($this->utf8StrRev($u, false, $this->tmprtl));
9245  $alias['a'][] = $this->_escape($this->UTF8ToLatin1($a));
9246  $alias['a'][] = $this->_escape($this->utf8StrRev($a, false, $this->tmprtl));
9247  }
9248  $alias['a'][] = $this->_escape($a);
9249  return $alias;
9250  }
9251 
9257  protected function getAllInternalPageNumberAliases() {
9258  $basic_alias = array($this->alias_tot_pages, $this->alias_num_page, $this->alias_group_tot_pages, $this->alias_group_num_page, $this->alias_right_shift);
9259  $pnalias = array();
9260  foreach($basic_alias as $k => $a) {
9261  $pnalias[$k] = $this->getInternalPageNumberAliases($a);
9262  }
9263  return $pnalias;
9264  }
9265 
9274  protected function replacePageNumAliases($page, $replace, $diff=0) {
9275  foreach ($replace as $rep) {
9276  foreach ($rep[3] as $a) {
9277  if (strpos($page, $a) !== false) {
9278  $page = str_replace($a, $rep[0], $page);
9279  $diff += ($rep[2] - $rep[1]);
9280  }
9281  }
9282  }
9283  return array($page, $diff);
9284  }
9285 
9295  protected function replaceRightShiftPageNumAliases($page, $aliases, $diff) {
9296  foreach ($aliases as $type => $alias) {
9297  foreach ($alias as $a) {
9298  // find position of compensation factor
9299  $startnum = (strpos($a, ':') + 1);
9300  $a = substr($a, 0, $startnum);
9301  if (($pos = strpos($page, $a)) !== false) {
9302  // end of alias
9303  $endnum = strpos($page, '}', $pos);
9304  // string to be replaced
9305  $aa = substr($page, $pos, ($endnum - $pos + 1));
9306  // get compensation factor
9307  $ratio = substr($page, ($pos + $startnum), ($endnum - $pos - $startnum));
9308  $ratio = preg_replace('/[^0-9\.]/', '', $ratio);
9309  $ratio = floatval($ratio);
9310  if ($type == 'u') {
9311  $chrdiff = floor(($diff + 12) * $ratio);
9312  $shift = str_repeat(' ', $chrdiff);
9313  $shift = $this->UTF8ToUTF16BE($shift, false);
9314  } else {
9315  $chrdiff = floor(($diff + 11) * $ratio);
9316  $shift = str_repeat(' ', $chrdiff);
9317  }
9318  $page = str_replace($aa, $shift, $page);
9319  }
9320  }
9321  }
9322  return $page;
9323  }
9324 
9330  protected function setPageBoxTypes($boxes) {
9331  $validboxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox');
9332  $this->page_boxes = array();
9333  foreach ($boxes as $box) {
9334  if (in_array($box, $validboxes)) {
9335  $this->page_boxes[] = $box;
9336  }
9337  }
9338  }
9339 
9344  protected function _putpages() {
9345  $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
9346  // get internal aliases for page numbers
9347  $pnalias = $this->getAllInternalPageNumberAliases();
9348  $num_pages = $this->numpages;
9349  $ptpa = $this->formatPageNumber(($this->starting_page_number + $num_pages - 1));
9350  $ptpu = $this->UTF8ToUTF16BE($ptpa, false);
9351  $ptp_num_chars = $this->GetNumChars($ptpa);
9352  $pagegroupnum = 0;
9353  $groupnum = 0;
9354  $ptgu = 1;
9355  $ptga = 1;
9356  for ($n = 1; $n <= $num_pages; ++$n) {
9357  // get current page
9358  $temppage = $this->getPageBuffer($n);
9359  $pagelen = strlen($temppage);
9360  // set replacements for total pages number
9361  $pnpa = $this->formatPageNumber(($this->starting_page_number + $n - 1));
9362  $pnpu = $this->UTF8ToUTF16BE($pnpa, false);
9363  $pnp_num_chars = $this->GetNumChars($pnpa);
9364  $pdiff = 0; // difference used for right shift alignment of page numbers
9365  $gdiff = 0; // difference used for right shift alignment of page group numbers
9366  if (!empty($this->pagegroups)) {
9367  if (isset($this->newpagegroup[$n])) {
9368  $pagegroupnum = 0;
9369  ++$groupnum;
9370  $ptga = $this->formatPageNumber($this->pagegroups[$groupnum]);
9371  $ptgu = $this->UTF8ToUTF16BE($ptga, false);
9372  $ptg_num_chars = $this->GetNumChars($ptga);
9373  }
9374  ++$pagegroupnum;
9375  $pnga = $this->formatPageNumber($pagegroupnum);
9376  $pngu = $this->UTF8ToUTF16BE($pnga, false);
9377  $png_num_chars = $this->GetNumChars($pnga);
9378  // replace page numbers
9379  $replace = array();
9380  $replace[] = array($ptgu, $ptg_num_chars, 9, $pnalias[2]['u']);
9381  $replace[] = array($ptga, $ptg_num_chars, 7, $pnalias[2]['a']);
9382  $replace[] = array($pngu, $png_num_chars, 9, $pnalias[3]['u']);
9383  $replace[] = array($pnga, $png_num_chars, 7, $pnalias[3]['a']);
9384  list($temppage, $gdiff) = $this->replacePageNumAliases($temppage, $replace, $gdiff);
9385  }
9386  // replace page numbers
9387  $replace = array();
9388  $replace[] = array($ptpu, $ptp_num_chars, 9, $pnalias[0]['u']);
9389  $replace[] = array($ptpa, $ptp_num_chars, 7, $pnalias[0]['a']);
9390  $replace[] = array($pnpu, $pnp_num_chars, 9, $pnalias[1]['u']);
9391  $replace[] = array($pnpa, $pnp_num_chars, 7, $pnalias[1]['a']);
9392  list($temppage, $pdiff) = $this->replacePageNumAliases($temppage, $replace, $pdiff);
9393  // replace right shift alias
9394  $temppage = $this->replaceRightShiftPageNumAliases($temppage, $pnalias[4], max($pdiff, $gdiff));
9395  // replace EPS marker
9396  $temppage = str_replace($this->epsmarker, '', $temppage);
9397  //Page
9398  $this->page_obj_id[$n] = $this->_newobj();
9399  $out = '<<';
9400  $out .= ' /Type /Page';
9401  $out .= ' /Parent 1 0 R';
9402  $out .= ' /LastModified '.$this->_datestring(0, $this->doc_modification_timestamp);
9403  $out .= ' /Resources 2 0 R';
9404  foreach ($this->page_boxes as $box) {
9405  $out .= ' /'.$box;
9406  $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']);
9407  }
9408  if (isset($this->pagedim[$n]['BoxColorInfo']) AND !empty($this->pagedim[$n]['BoxColorInfo'])) {
9409  $out .= ' /BoxColorInfo <<';
9410  foreach ($this->page_boxes as $box) {
9411  if (isset($this->pagedim[$n]['BoxColorInfo'][$box])) {
9412  $out .= ' /'.$box.' <<';
9413  if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['C'])) {
9414  $color = $this->pagedim[$n]['BoxColorInfo'][$box]['C'];
9415  $out .= ' /C [';
9416  $out .= sprintf(' %F %F %F', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255));
9417  $out .= ' ]';
9418  }
9419  if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['W'])) {
9420  $out .= ' /W '.($this->pagedim[$n]['BoxColorInfo'][$box]['W'] * $this->k);
9421  }
9422  if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['S'])) {
9423  $out .= ' /S /'.$this->pagedim[$n]['BoxColorInfo'][$box]['S'];
9424  }
9425  if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['D'])) {
9426  $dashes = $this->pagedim[$n]['BoxColorInfo'][$box]['D'];
9427  $out .= ' /D [';
9428  foreach ($dashes as $dash) {
9429  $out .= sprintf(' %F', ($dash * $this->k));
9430  }
9431  $out .= ' ]';
9432  }
9433  $out .= ' >>';
9434  }
9435  }
9436  $out .= ' >>';
9437  }
9438  $out .= ' /Contents '.($this->n + 1).' 0 R';
9439  $out .= ' /Rotate '.$this->pagedim[$n]['Rotate'];
9440  if (!$this->pdfa_mode) {
9441  $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceRGB >>';
9442  }
9443  if (isset($this->pagedim[$n]['trans']) AND !empty($this->pagedim[$n]['trans'])) {
9444  // page transitions
9445  if (isset($this->pagedim[$n]['trans']['Dur'])) {
9446  $out .= ' /Dur '.$this->pagedim[$n]['trans']['Dur'];
9447  }
9448  $out .= ' /Trans <<';
9449  $out .= ' /Type /Trans';
9450  if (isset($this->pagedim[$n]['trans']['S'])) {
9451  $out .= ' /S /'.$this->pagedim[$n]['trans']['S'];
9452  }
9453  if (isset($this->pagedim[$n]['trans']['D'])) {
9454  $out .= ' /D '.$this->pagedim[$n]['trans']['D'];
9455  }
9456  if (isset($this->pagedim[$n]['trans']['Dm'])) {
9457  $out .= ' /Dm /'.$this->pagedim[$n]['trans']['Dm'];
9458  }
9459  if (isset($this->pagedim[$n]['trans']['M'])) {
9460  $out .= ' /M /'.$this->pagedim[$n]['trans']['M'];
9461  }
9462  if (isset($this->pagedim[$n]['trans']['Di'])) {
9463  $out .= ' /Di '.$this->pagedim[$n]['trans']['Di'];
9464  }
9465  if (isset($this->pagedim[$n]['trans']['SS'])) {
9466  $out .= ' /SS '.$this->pagedim[$n]['trans']['SS'];
9467  }
9468  if (isset($this->pagedim[$n]['trans']['B'])) {
9469  $out .= ' /B '.$this->pagedim[$n]['trans']['B'];
9470  }
9471  $out .= ' >>';
9472  }
9473  $out .= $this->_getannotsrefs($n);
9474  $out .= ' /PZ '.$this->pagedim[$n]['PZ'];
9475  $out .= ' >>';
9476  $out .= "\n".'endobj';
9477  $this->_out($out);
9478  //Page content
9479  $p = ($this->compress) ? gzcompress($temppage) : $temppage;
9480  $this->_newobj();
9481  $p = $this->_getrawstream($p);
9482  $this->_out('<<'.$filter.'/Length '.strlen($p).'>> stream'."\n".$p."\n".'endstream'."\n".'endobj');
9483  if ($this->diskcache) {
9484  // remove temporary files
9485  unlink($this->pages[$n]);
9486  }
9487  }
9488  //Pages root
9489  $out = $this->_getobj(1)."\n";
9490  $out .= '<< /Type /Pages /Kids [';
9491  foreach($this->page_obj_id as $page_obj) {
9492  $out .= ' '.$page_obj.' 0 R';
9493  }
9494  $out .= ' ] /Count '.$num_pages.' >>';
9495  $out .= "\n".'endobj';
9496  $this->_out($out);
9497  }
9498 
9507  protected function _putannotsrefs($n) {
9508  $this->_out($this->_getannotsrefs($n));
9509  }
9510 
9519  protected function _getannotsrefs($n) {
9520  if (!(isset($this->PageAnnots[$n]) OR ($this->sign AND isset($this->signature_data['cert_type'])))) {
9521  return '';
9522  }
9523  $out = ' /Annots [';
9524  if (isset($this->PageAnnots[$n])) {
9525  foreach ($this->PageAnnots[$n] as $key => $val) {
9526  if (!in_array($val['n'], $this->radio_groups)) {
9527  $out .= ' '.$val['n'].' 0 R';
9528  }
9529  }
9530  // add radiobutton groups
9531  if (isset($this->radiobutton_groups[$n])) {
9532  foreach ($this->radiobutton_groups[$n] as $key => $data) {
9533  if (isset($data['n'])) {
9534  $out .= ' '.$data['n'].' 0 R';
9535  }
9536  }
9537  }
9538  }
9539  if ($this->sign AND ($n == $this->signature_appearance['page']) AND isset($this->signature_data['cert_type'])) {
9540  // set reference for signature object
9541  $out .= ' '.$this->sig_obj_id.' 0 R';
9542  }
9543  if (!empty($this->empty_signature_appearance)) {
9544  foreach ($this->empty_signature_appearance as $esa) {
9545  if ($esa['page'] == $n) {
9546  // set reference for empty signature objects
9547  $out .= ' '.$esa['objid'].' 0 R';
9548  }
9549  }
9550  }
9551  $out .= ' ]';
9552  return $out;
9553  }
9554 
9563  protected function _putannotsobjs() {
9564  // reset object counter
9565  for ($n=1; $n <= $this->numpages; ++$n) {
9566  if (isset($this->PageAnnots[$n])) {
9567  // set page annotations
9568  foreach ($this->PageAnnots[$n] as $key => $pl) {
9569  $annot_obj_id = $this->PageAnnots[$n][$key]['n'];
9570  // create annotation object for grouping radiobuttons
9571  if (isset($this->radiobutton_groups[$n][$pl['txt']]) AND is_array($this->radiobutton_groups[$n][$pl['txt']])) {
9572  $radio_button_obj_id = $this->radiobutton_groups[$n][$pl['txt']]['n'];
9573  $annots = '<<';
9574  $annots .= ' /Type /Annot';
9575  $annots .= ' /Subtype /Widget';
9576  $annots .= ' /Rect [0 0 0 0]';
9577  if ($this->radiobutton_groups[$n][$pl['txt']]['#readonly#']) {
9578  // read only
9579  $annots .= ' /F 68';
9580  $annots .= ' /Ff 49153';
9581  } else {
9582  $annots .= ' /F 4'; // default print for PDF/A
9583  $annots .= ' /Ff 49152';
9584  }
9585  $annots .= ' /T '.$this->_datastring($pl['txt'], $radio_button_obj_id);
9586  if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
9587  $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $radio_button_obj_id);
9588  }
9589  $annots .= ' /FT /Btn';
9590  $annots .= ' /Kids [';
9591  $defval = '';
9592  foreach ($this->radiobutton_groups[$n][$pl['txt']] as $key => $data) {
9593  if (isset($data['kid'])) {
9594  $annots .= ' '.$data['kid'].' 0 R';
9595  if ($data['def'] !== 'Off') {
9596  $defval = $data['def'];
9597  }
9598  }
9599  }
9600  $annots .= ' ]';
9601  if (!empty($defval)) {
9602  $annots .= ' /V /'.$defval;
9603  }
9604  $annots .= ' >>';
9605  $this->_out($this->_getobj($radio_button_obj_id)."\n".$annots."\n".'endobj');
9606  $this->form_obj_id[] = $radio_button_obj_id;
9607  // store object id to be used on Parent entry of Kids
9608  $this->radiobutton_groups[$n][$pl['txt']] = $radio_button_obj_id;
9609  }
9610  $formfield = false;
9611  $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER);
9612  $a = $pl['x'] * $this->k;
9613  $b = $this->pagedim[$n]['h'] - (($pl['y'] + $pl['h']) * $this->k);
9614  $c = $pl['w'] * $this->k;
9615  $d = $pl['h'] * $this->k;
9616  $rect = sprintf('%F %F %F %F', $a, $b, $a+$c, $b+$d);
9617  // create new annotation object
9618  $annots = '<</Type /Annot';
9619  $annots .= ' /Subtype /'.$pl['opt']['subtype'];
9620  $annots .= ' /Rect ['.$rect.']';
9621  $ft = array('Btn', 'Tx', 'Ch', 'Sig');
9622  if (isset($pl['opt']['ft']) AND in_array($pl['opt']['ft'], $ft)) {
9623  $annots .= ' /FT /'.$pl['opt']['ft'];
9624  $formfield = true;
9625  }
9626  $annots .= ' /Contents '.$this->_textstring($pl['txt'], $annot_obj_id);
9627  $annots .= ' /P '.$this->page_obj_id[$n].' 0 R';
9628  $annots .= ' /NM '.$this->_datastring(sprintf('%04u-%04u', $n, $key), $annot_obj_id);
9629  $annots .= ' /M '.$this->_datestring($annot_obj_id, $this->doc_modification_timestamp);
9630  if (isset($pl['opt']['f'])) {
9631  $fval = 0;
9632  if (is_array($pl['opt']['f'])) {
9633  foreach ($pl['opt']['f'] as $f) {
9634  switch (strtolower($f)) {
9635  case 'invisible': {
9636  $fval += 1 << 0;
9637  break;
9638  }
9639  case 'hidden': {
9640  $fval += 1 << 1;
9641  break;
9642  }
9643  case 'print': {
9644  $fval += 1 << 2;
9645  break;
9646  }
9647  case 'nozoom': {
9648  $fval += 1 << 3;
9649  break;
9650  }
9651  case 'norotate': {
9652  $fval += 1 << 4;
9653  break;
9654  }
9655  case 'noview': {
9656  $fval += 1 << 5;
9657  break;
9658  }
9659  case 'readonly': {
9660  $fval += 1 << 6;
9661  break;
9662  }
9663  case 'locked': {
9664  $fval += 1 << 8;
9665  break;
9666  }
9667  case 'togglenoview': {
9668  $fval += 1 << 9;
9669  break;
9670  }
9671  case 'lockedcontents': {
9672  $fval += 1 << 10;
9673  break;
9674  }
9675  default: {
9676  break;
9677  }
9678  }
9679  }
9680  } else {
9681  $fval = intval($pl['opt']['f']);
9682  }
9683  } else {
9684  $fval = 4;
9685  }
9686  if ($this->pdfa_mode) {
9687  // force print flag for PDF/A mode
9688  $fval |= 4;
9689  }
9690  $annots .= ' /F '.intval($fval);
9691  if (isset($pl['opt']['as']) AND is_string($pl['opt']['as'])) {
9692  $annots .= ' /AS /'.$pl['opt']['as'];
9693  }
9694  if (isset($pl['opt']['ap'])) {
9695  // appearance stream
9696  $annots .= ' /AP <<';
9697  if (is_array($pl['opt']['ap'])) {
9698  foreach ($pl['opt']['ap'] as $apmode => $apdef) {
9699  // $apmode can be: n = normal; r = rollover; d = down;
9700  $annots .= ' /'.strtoupper($apmode);
9701  if (is_array($apdef)) {
9702  $annots .= ' <<';
9703  foreach ($apdef as $apstate => $stream) {
9704  // reference to XObject that define the appearance for this mode-state
9705  $apsobjid = $this->_putAPXObject($c, $d, $stream);
9706  $annots .= ' /'.$apstate.' '.$apsobjid.' 0 R';
9707  }
9708  $annots .= ' >>';
9709  } else {
9710  // reference to XObject that define the appearance for this mode
9711  $apsobjid = $this->_putAPXObject($c, $d, $apdef);
9712  $annots .= ' '.$apsobjid.' 0 R';
9713  }
9714  }
9715  } else {
9716  $annots .= $pl['opt']['ap'];
9717  }
9718  $annots .= ' >>';
9719  }
9720  if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) {
9721  $annots .= ' /BS <<';
9722  $annots .= ' /Type /Border';
9723  if (isset($pl['opt']['bs']['w'])) {
9724  $annots .= ' /W '.intval($pl['opt']['bs']['w']);
9725  }
9726  $bstyles = array('S', 'D', 'B', 'I', 'U');
9727  if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $bstyles)) {
9728  $annots .= ' /S /'.$pl['opt']['bs']['s'];
9729  }
9730  if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) {
9731  $annots .= ' /D [';
9732  foreach ($pl['opt']['bs']['d'] as $cord) {
9733  $annots .= ' '.intval($cord);
9734  }
9735  $annots .= ']';
9736  }
9737  $annots .= ' >>';
9738  } else {
9739  $annots .= ' /Border [';
9740  if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) {
9741  $annots .= intval($pl['opt']['border'][0]).' ';
9742  $annots .= intval($pl['opt']['border'][1]).' ';
9743  $annots .= intval($pl['opt']['border'][2]);
9744  if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) {
9745  $annots .= ' [';
9746  foreach ($pl['opt']['border'][3] as $dash) {
9747  $annots .= intval($dash).' ';
9748  }
9749  $annots .= ']';
9750  }
9751  } else {
9752  $annots .= '0 0 0';
9753  }
9754  $annots .= ']';
9755  }
9756  if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) {
9757  $annots .= ' /BE <<';
9758  $bstyles = array('S', 'C');
9759  if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $bstyles)) {
9760  $annots .= ' /S /'.$pl['opt']['bs']['s'];
9761  } else {
9762  $annots .= ' /S /S';
9763  }
9764  if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) {
9765  $annots .= ' /I '.sprintf(' %F', $pl['opt']['be']['i']);
9766  }
9767  $annots .= '>>';
9768  }
9769  if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c'])) AND !empty($pl['opt']['c'])) {
9770  $annots .= ' /C '.$this->getColorStringFromArray($pl['opt']['c']);
9771  }
9772  //$annots .= ' /StructParent ';
9773  //$annots .= ' /OC ';
9774  $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight', 'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound');
9775  if (in_array(strtolower($pl['opt']['subtype']), $markups)) {
9776  // this is a markup type
9777  if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
9778  $annots .= ' /T '.$this->_textstring($pl['opt']['t'], $annot_obj_id);
9779  }
9780  //$annots .= ' /Popup ';
9781  if (isset($pl['opt']['ca'])) {
9782  $annots .= ' /CA '.sprintf('%F', floatval($pl['opt']['ca']));
9783  }
9784  if (isset($pl['opt']['rc'])) {
9785  $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
9786  }
9787  $annots .= ' /CreationDate '.$this->_datestring($annot_obj_id, $this->doc_creation_timestamp);
9788  //$annots .= ' /IRT ';
9789  if (isset($pl['opt']['subj'])) {
9790  $annots .= ' /Subj '.$this->_textstring($pl['opt']['subj'], $annot_obj_id);
9791  }
9792  //$annots .= ' /RT ';
9793  //$annots .= ' /IT ';
9794  //$annots .= ' /ExData ';
9795  }
9796  $lineendings = array('Square', 'Circle', 'Diamond', 'OpenArrow', 'ClosedArrow', 'None', 'Butt', 'ROpenArrow', 'RClosedArrow', 'Slash');
9797  // Annotation types
9798  switch (strtolower($pl['opt']['subtype'])) {
9799  case 'text': {
9800  if (isset($pl['opt']['open'])) {
9801  $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false');
9802  }
9803  $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph');
9804  if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
9805  $annots .= ' /Name /'.$pl['opt']['name'];
9806  } else {
9807  $annots .= ' /Name /Note';
9808  }
9809  $statemodels = array('Marked', 'Review');
9810  if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) {
9811  $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
9812  } else {
9813  $pl['opt']['statemodel'] = 'Marked';
9814  $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
9815  }
9816  if ($pl['opt']['statemodel'] == 'Marked') {
9817  $states = array('Accepted', 'Unmarked');
9818  } else {
9819  $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None');
9820  }
9821  if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) {
9822  $annots .= ' /State /'.$pl['opt']['state'];
9823  } else {
9824  if ($pl['opt']['statemodel'] == 'Marked') {
9825  $annots .= ' /State /Unmarked';
9826  } else {
9827  $annots .= ' /State /None';
9828  }
9829  }
9830  break;
9831  }
9832  case 'link': {
9833  if (is_string($pl['txt'])) {
9834  if ($pl['txt'][0] == '#') {
9835  // internal destination
9836  $annots .= ' /Dest /'.$this->encodeNameObject(substr($pl['txt'], 1));
9837  } else {
9838  // external URI link
9839  $annots .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($pl['txt']), $annot_obj_id).'>>';
9840  }
9841  } else {
9842  // internal link
9843  if (isset($this->links[$pl['txt']])) {
9844  $l = $this->links[$pl['txt']];
9845  if (isset($this->page_obj_id[($l[0])])) {
9846  $annots .= sprintf(' /Dest [%u 0 R /XYZ 0 %F null]', $this->page_obj_id[($l[0])], ($this->pagedim[$l[0]]['h'] - ($l[1] * $this->k)));
9847  }
9848  }
9849  }
9850  $hmodes = array('N', 'I', 'O', 'P');
9851  if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) {
9852  $annots .= ' /H /'.$pl['opt']['h'];
9853  } else {
9854  $annots .= ' /H /I';
9855  }
9856  //$annots .= ' /PA ';
9857  //$annots .= ' /Quadpoints ';
9858  break;
9859  }
9860  case 'freetext': {
9861  if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
9862  $annots .= ' /DA ('.$pl['opt']['da'].')';
9863  }
9864  if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
9865  $annots .= ' /Q '.intval($pl['opt']['q']);
9866  }
9867  if (isset($pl['opt']['rc'])) {
9868  $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
9869  }
9870  if (isset($pl['opt']['ds'])) {
9871  $annots .= ' /DS '.$this->_textstring($pl['opt']['ds'], $annot_obj_id);
9872  }
9873  if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) {
9874  $annots .= ' /CL [';
9875  foreach ($pl['opt']['cl'] as $cl) {
9876  $annots .= sprintf('%F ', $cl * $this->k);
9877  }
9878  $annots .= ']';
9879  }
9880  $tfit = array('FreeText', 'FreeTextCallout', 'FreeTextTypeWriter');
9881  if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) {
9882  $annots .= ' /IT /'.$pl['opt']['it'];
9883  }
9884  if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) {
9885  $l = $pl['opt']['rd'][0] * $this->k;
9886  $r = $pl['opt']['rd'][1] * $this->k;
9887  $t = $pl['opt']['rd'][2] * $this->k;
9888  $b = $pl['opt']['rd'][3] * $this->k;
9889  $annots .= ' /RD ['.sprintf('%F %F %F %F', $l, $r, $t, $b).']';
9890  }
9891  if (isset($pl['opt']['le']) AND in_array($pl['opt']['le'], $lineendings)) {
9892  $annots .= ' /LE /'.$pl['opt']['le'];
9893  }
9894  break;
9895  }
9896  case 'line': {
9897  break;
9898  }
9899  case 'square': {
9900  break;
9901  }
9902  case 'circle': {
9903  break;
9904  }
9905  case 'polygon': {
9906  break;
9907  }
9908  case 'polyline': {
9909  break;
9910  }
9911  case 'highlight': {
9912  break;
9913  }
9914  case 'underline': {
9915  break;
9916  }
9917  case 'squiggly': {
9918  break;
9919  }
9920  case 'strikeout': {
9921  break;
9922  }
9923  case 'stamp': {
9924  break;
9925  }
9926  case 'caret': {
9927  break;
9928  }
9929  case 'ink': {
9930  break;
9931  }
9932  case 'popup': {
9933  break;
9934  }
9935  case 'fileattachment': {
9936  if ($this->pdfa_mode) {
9937  // embedded files are not allowed in PDF/A mode
9938  break;
9939  }
9940  if (!isset($pl['opt']['fs'])) {
9941  break;
9942  }
9943  $filename = basename($pl['opt']['fs']);
9944  if (isset($this->embeddedfiles[$filename]['n'])) {
9945  $annots .= ' /FS <</Type /Filespec /F '.$this->_datastring($filename, $annot_obj_id).' /EF <</F '.$this->embeddedfiles[$filename]['n'].' 0 R>> >>';
9946  $iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag');
9947  if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
9948  $annots .= ' /Name /'.$pl['opt']['name'];
9949  } else {
9950  $annots .= ' /Name /PushPin';
9951  }
9952  }
9953  break;
9954  }
9955  case 'sound': {
9956  if (!isset($pl['opt']['fs'])) {
9957  break;
9958  }
9959  $filename = basename($pl['opt']['fs']);
9960  if (isset($this->embeddedfiles[$filename]['n'])) {
9961  // ... TO BE COMPLETED ...
9962  // /R /C /B /E /CO /CP
9963  $annots .= ' /Sound <</Type /Filespec /F '.$this->_datastring($filename, $annot_obj_id).' /EF <</F '.$this->embeddedfiles[$filename]['n'].' 0 R>> >>';
9964  $iconsapp = array('Speaker', 'Mic');
9965  if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
9966  $annots .= ' /Name /'.$pl['opt']['name'];
9967  } else {
9968  $annots .= ' /Name /Speaker';
9969  }
9970  }
9971  break;
9972  }
9973  case 'movie': {
9974  break;
9975  }
9976  case 'widget': {
9977  $hmode = array('N', 'I', 'O', 'P', 'T');
9978  if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmode)) {
9979  $annots .= ' /H /'.$pl['opt']['h'];
9980  }
9981  if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk'])) AND !empty($pl['opt']['mk'])) {
9982  $annots .= ' /MK <<';
9983  if (isset($pl['opt']['mk']['r'])) {
9984  $annots .= ' /R '.$pl['opt']['mk']['r'];
9985  }
9986  if (isset($pl['opt']['mk']['bc']) AND (is_array($pl['opt']['mk']['bc']))) {
9987  $annots .= ' /BC '.$this->getColorStringFromArray($pl['opt']['mk']['bc']);
9988  }
9989  if (isset($pl['opt']['mk']['bg']) AND (is_array($pl['opt']['mk']['bg']))) {
9990  $annots .= ' /BG '.$this->getColorStringFromArray($pl['opt']['mk']['bg']);
9991  }
9992  if (isset($pl['opt']['mk']['ca'])) {
9993  $annots .= ' /CA '.$pl['opt']['mk']['ca'];
9994  }
9995  if (isset($pl['opt']['mk']['rc'])) {
9996  $annots .= ' /RC '.$pl['opt']['mk']['rc'];
9997  }
9998  if (isset($pl['opt']['mk']['ac'])) {
9999  $annots .= ' /AC '.$pl['opt']['mk']['ac'];
10000  }
10001  if (isset($pl['opt']['mk']['i'])) {
10002  $info = $this->getImageBuffer($pl['opt']['mk']['i']);
10003  if ($info !== false) {
10004  $annots .= ' /I '.$info['n'].' 0 R';
10005  }
10006  }
10007  if (isset($pl['opt']['mk']['ri'])) {
10008  $info = $this->getImageBuffer($pl['opt']['mk']['ri']);
10009  if ($info !== false) {
10010  $annots .= ' /RI '.$info['n'].' 0 R';
10011  }
10012  }
10013  if (isset($pl['opt']['mk']['ix'])) {
10014  $info = $this->getImageBuffer($pl['opt']['mk']['ix']);
10015  if ($info !== false) {
10016  $annots .= ' /IX '.$info['n'].' 0 R';
10017  }
10018  }
10019  if (isset($pl['opt']['mk']['if']) AND (is_array($pl['opt']['mk']['if'])) AND !empty($pl['opt']['mk']['if'])) {
10020  $annots .= ' /IF <<';
10021  $if_sw = array('A', 'B', 'S', 'N');
10022  if (isset($pl['opt']['mk']['if']['sw']) AND in_array($pl['opt']['mk']['if']['sw'], $if_sw)) {
10023  $annots .= ' /SW /'.$pl['opt']['mk']['if']['sw'];
10024  }
10025  $if_s = array('A', 'P');
10026  if (isset($pl['opt']['mk']['if']['s']) AND in_array($pl['opt']['mk']['if']['s'], $if_s)) {
10027  $annots .= ' /S /'.$pl['opt']['mk']['if']['s'];
10028  }
10029  if (isset($pl['opt']['mk']['if']['a']) AND (is_array($pl['opt']['mk']['if']['a'])) AND !empty($pl['opt']['mk']['if']['a'])) {
10030  $annots .= sprintf(' /A [%F %F]', $pl['opt']['mk']['if']['a'][0], $pl['opt']['mk']['if']['a'][1]);
10031  }
10032  if (isset($pl['opt']['mk']['if']['fb']) AND ($pl['opt']['mk']['if']['fb'])) {
10033  $annots .= ' /FB true';
10034  }
10035  $annots .= '>>';
10036  }
10037  if (isset($pl['opt']['mk']['tp']) AND ($pl['opt']['mk']['tp'] >= 0) AND ($pl['opt']['mk']['tp'] <= 6)) {
10038  $annots .= ' /TP '.intval($pl['opt']['mk']['tp']);
10039  }
10040  $annots .= '>>';
10041  } // end MK
10042  // --- Entries for field dictionaries ---
10043  if (isset($this->radiobutton_groups[$n][$pl['txt']])) {
10044  // set parent
10045  $annots .= ' /Parent '.$this->radiobutton_groups[$n][$pl['txt']].' 0 R';
10046  }
10047  if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
10048  $annots .= ' /T '.$this->_datastring($pl['opt']['t'], $annot_obj_id);
10049  }
10050  if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
10051  $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $annot_obj_id);
10052  }
10053  if (isset($pl['opt']['tm']) AND is_string($pl['opt']['tm'])) {
10054  $annots .= ' /TM '.$this->_datastring($pl['opt']['tm'], $annot_obj_id);
10055  }
10056  if (isset($pl['opt']['ff'])) {
10057  if (is_array($pl['opt']['ff'])) {
10058  // array of bit settings
10059  $flag = 0;
10060  foreach($pl['opt']['ff'] as $val) {
10061  $flag += 1 << ($val - 1);
10062  }
10063  } else {
10064  $flag = intval($pl['opt']['ff']);
10065  }
10066  $annots .= ' /Ff '.$flag;
10067  }
10068  if (isset($pl['opt']['maxlen'])) {
10069  $annots .= ' /MaxLen '.intval($pl['opt']['maxlen']);
10070  }
10071  if (isset($pl['opt']['v'])) {
10072  $annots .= ' /V';
10073  if (is_array($pl['opt']['v'])) {
10074  foreach ($pl['opt']['v'] AS $optval) {
10075  if (is_float($optval)) {
10076  $optval = sprintf('%F', $optval);
10077  }
10078  $annots .= ' '.$optval;
10079  }
10080  } else {
10081  $annots .= ' '.$this->_textstring($pl['opt']['v'], $annot_obj_id);
10082  }
10083  }
10084  if (isset($pl['opt']['dv'])) {
10085  $annots .= ' /DV';
10086  if (is_array($pl['opt']['dv'])) {
10087  foreach ($pl['opt']['dv'] AS $optval) {
10088  if (is_float($optval)) {
10089  $optval = sprintf('%F', $optval);
10090  }
10091  $annots .= ' '.$optval;
10092  }
10093  } else {
10094  $annots .= ' '.$this->_textstring($pl['opt']['dv'], $annot_obj_id);
10095  }
10096  }
10097  if (isset($pl['opt']['rv'])) {
10098  $annots .= ' /RV';
10099  if (is_array($pl['opt']['rv'])) {
10100  foreach ($pl['opt']['rv'] AS $optval) {
10101  if (is_float($optval)) {
10102  $optval = sprintf('%F', $optval);
10103  }
10104  $annots .= ' '.$optval;
10105  }
10106  } else {
10107  $annots .= ' '.$this->_textstring($pl['opt']['rv'], $annot_obj_id);
10108  }
10109  }
10110  if (isset($pl['opt']['a']) AND !empty($pl['opt']['a'])) {
10111  $annots .= ' /A << '.$pl['opt']['a'].' >>';
10112  }
10113  if (isset($pl['opt']['aa']) AND !empty($pl['opt']['aa'])) {
10114  $annots .= ' /AA << '.$pl['opt']['aa'].' >>';
10115  }
10116  if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
10117  $annots .= ' /DA ('.$pl['opt']['da'].')';
10118  }
10119  if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
10120  $annots .= ' /Q '.intval($pl['opt']['q']);
10121  }
10122  if (isset($pl['opt']['opt']) AND (is_array($pl['opt']['opt'])) AND !empty($pl['opt']['opt'])) {
10123  $annots .= ' /Opt [';
10124  foreach($pl['opt']['opt'] AS $copt) {
10125  if (is_array($copt)) {
10126  $annots .= ' ['.$this->_textstring($copt[0], $annot_obj_id).' '.$this->_textstring($copt[1], $annot_obj_id).']';
10127  } else {
10128  $annots .= ' '.$this->_textstring($copt, $annot_obj_id);
10129  }
10130  }
10131  $annots .= ']';
10132  }
10133  if (isset($pl['opt']['ti'])) {
10134  $annots .= ' /TI '.intval($pl['opt']['ti']);
10135  }
10136  if (isset($pl['opt']['i']) AND (is_array($pl['opt']['i'])) AND !empty($pl['opt']['i'])) {
10137  $annots .= ' /I [';
10138  foreach($pl['opt']['i'] AS $copt) {
10139  $annots .= intval($copt).' ';
10140  }
10141  $annots .= ']';
10142  }
10143  break;
10144  }
10145  case 'screen': {
10146  break;
10147  }
10148  case 'printermark': {
10149  break;
10150  }
10151  case 'trapnet': {
10152  break;
10153  }
10154  case 'watermark': {
10155  break;
10156  }
10157  case '3d': {
10158  break;
10159  }
10160  default: {
10161  break;
10162  }
10163  }
10164  $annots .= '>>';
10165  // create new annotation object
10166  $this->_out($this->_getobj($annot_obj_id)."\n".$annots."\n".'endobj');
10167  if ($formfield AND !isset($this->radiobutton_groups[$n][$pl['txt']])) {
10168  // store reference of form object
10169  $this->form_obj_id[] = $annot_obj_id;
10170  }
10171  }
10172  }
10173  } // end for each page
10174  }
10175 
10185  protected function _putAPXObject($w=0, $h=0, $stream='') {
10186  $stream = trim($stream);
10187  $out = $this->_getobj()."\n";
10188  $this->xobjects['AX'.$this->n] = array('n' => $this->n);
10189  $out .= '<<';
10190  $out .= ' /Type /XObject';
10191  $out .= ' /Subtype /Form';
10192  $out .= ' /FormType 1';
10193  if ($this->compress) {
10194  $stream = gzcompress($stream);
10195  $out .= ' /Filter /FlateDecode';
10196  }
10197  $rect = sprintf('%F %F', $w, $h);
10198  $out .= ' /BBox [0 0 '.$rect.']';
10199  $out .= ' /Matrix [1 0 0 1 0 0]';
10200  $out .= ' /Resources 2 0 R';
10201  $stream = $this->_getrawstream($stream);
10202  $out .= ' /Length '.strlen($stream);
10203  $out .= ' >>';
10204  $out .= ' stream'."\n".$stream."\n".'endstream';
10205  $out .= "\n".'endobj';
10206  $this->_out($out);
10207  return $this->n;
10208  }
10209 
10219  protected function _getULONG($str, $offset) {
10220  $v = unpack('Ni', substr($str, $offset, 4));
10221  return $v['i'];
10222  }
10223 
10233  protected function _getUSHORT($str, $offset) {
10234  $v = unpack('ni', substr($str, $offset, 2));
10235  return $v['i'];
10236  }
10237 
10247  protected function _getSHORT($str, $offset) {
10248  $v = unpack('si', substr($str, $offset, 2));
10249  return $v['i'];
10250  }
10251 
10261  protected function _getFWORD($str, $offset) {
10262  $v = $this->_getUSHORT($str, $offset);
10263  if ($v > 0x7fff) {
10264  $v -= 0x10000;
10265  }
10266  return $v;
10267  }
10268 
10278  protected function _getUFWORD($str, $offset) {
10279  $v = $this->_getUSHORT($str, $offset);
10280  return $v;
10281  }
10282 
10292  protected function _getFIXED($str, $offset) {
10293  // mantissa
10294  $m = $this->_getFWORD($str, $offset);
10295  // fraction
10296  $f = $this->_getUSHORT($str, ($offset + 2));
10297  $v = floatval(''.$m.'.'.$f.'');
10298  return $v;
10299  }
10300 
10310  protected function _getBYTE($str, $offset) {
10311  $v = unpack('Ci', substr($str, $offset, 1));
10312  return $v['i'];
10313  }
10324  protected function updateCIDtoGIDmap($map, $cid, $gid) {
10325  if (($cid >= 0) AND ($cid <= 0xFFFF) AND ($gid >= 0)) {
10326  if ($gid > 0xFFFF) {
10327  $gid -= 0x10000;
10328  }
10329  $map[($cid * 2)] = chr($gid >> 8);
10330  $map[(($cid * 2) + 1)] = chr($gid & 0xFF);
10331  }
10332  return $map;
10333  }
10334 
10350  public function addTTFfont($fontfile, $fonttype='', $enc='', $flags=32, $outpath='', $platid=3, $encid=1, $addcbbox=false) {
10351  if (!file_exists($fontfile)) {
10352  $this->Error('Could not find file: '.$fontfile.'');
10353  }
10354  // font metrics
10355  $fmetric = array();
10356  // build new font name for TCPDF compatibility
10357  $font_path_parts = pathinfo($fontfile);
10358  if (!isset($font_path_parts['filename'])) {
10359  $font_path_parts['filename'] = substr($font_path_parts['basename'], 0, -(strlen($font_path_parts['extension']) + 1));
10360  }
10361  $font_name = strtolower($font_path_parts['filename']);
10362  $font_name = preg_replace('/[^a-z0-9_]/', '', $font_name);
10363  $search = array('bold', 'oblique', 'italic', 'regular');
10364  $replace = array('b', 'i', 'i', '');
10365  $font_name = str_replace($search, $replace, $font_name);
10366  if (empty($font_name)) {
10367  // set generic name
10368  $font_name = 'tcpdffont';
10369  }
10370  // set output path
10371  if (empty($outpath)) {
10372  $outpath = $this->_getfontpath();
10373  }
10374  // check if this font already exist
10375  if (file_exists($outpath.$font_name.'.php')) {
10376  // this font already exist (delete it from fonts folder to rebuild it)
10377  return $font_name;
10378  }
10379  $fmetric['file'] = $font_name.'.z';
10380  $fmetric['ctg'] = $font_name.'.ctg.z';
10381  // get font data
10382  $font = file_get_contents($fontfile);
10383  $fmetric['originalsize'] = strlen($font);
10384  // autodetect font type
10385  if (empty($fonttype)) {
10386  if ($this->_getULONG($font, 0) == 0x10000) {
10387  // True Type (Unicode or not)
10388  $fonttype = 'TrueTypeUnicode';
10389  } elseif (substr($font, 0, 4) == 'OTTO') {
10390  // Open Type (Unicode or not)
10391  $this->Error('Unsupported font format: OpenType with CFF data.');
10392  } else {
10393  // Type 1
10394  $fonttype = 'Type1';
10395  }
10396  }
10397  // set font type
10398  switch ($fonttype) {
10399  case 'CID0CT':
10400  case 'CID0CS':
10401  case 'CID0KR':
10402  case 'CID0JP': {
10403  $fmetric['type'] = 'cidfont0';
10404  break;
10405  }
10406  case 'Type1': {
10407  $fmetric['type'] = 'Type1';
10408  if (empty($enc) AND (($flags & 4) == 0)) {
10409  $enc = 'cp1252';
10410  }
10411  break;
10412  }
10413  case 'TrueType': {
10414  $fmetric['type'] = 'TrueType';
10415  break;
10416  }
10417  case 'TrueTypeUnicode':
10418  default: {
10419  $fmetric['type'] = 'TrueTypeUnicode';
10420  break;
10421  }
10422  }
10423  // set encoding maps (if any)
10424  $fmetric['enc'] = preg_replace('/[^A-Za-z0-9_\-]/', '', $enc);
10425  $fmetric['diff'] = '';
10426  if (($fmetric['type'] == 'TrueType') OR ($fmetric['type'] == 'Type1')) {
10427  if (!empty($enc) AND ($enc != 'cp1252') AND isset($this->encmaps->encmap[$enc])) {
10428  // build differences from reference encoding
10429  $enc_ref = $this->encmaps->encmap['cp1252'];
10430  $enc_target = $this->encmaps->encmap[$enc];
10431  $last = 0;
10432  for ($i = 32; $i <= 255; ++$i) {
10433  if ($enc_target != $enc_ref[$i]) {
10434  if ($i != ($last + 1)) {
10435  $fmetric['diff'] .= $i.' ';
10436  }
10437  $last = $i;
10438  $fmetric['diff'] .= '/'.$enc_target[$i].' ';
10439  }
10440  }
10441  }
10442  }
10443  // parse the font by type
10444  if ($fmetric['type'] == 'Type1') {
10445  // ---------- TYPE 1 ----------
10446  // read first segment
10447  $a = unpack('Cmarker/Ctype/Vsize', substr($font, 0, 6));
10448  if ($a['marker'] != 128) {
10449  $this->Error('Font file is not a valid binary Type1');
10450  }
10451  $fmetric['size1'] = $a['size'];
10452  $data = substr($font, 6, $fmetric['size1']);
10453  // read second segment
10454  $a = unpack('Cmarker/Ctype/Vsize', substr($font, (6 + $fmetric['size1']), 6));
10455  if ($a['marker'] != 128) {
10456  $this->Error('Font file is not a valid binary Type1');
10457  }
10458  $fmetric['size2'] = $a['size'];
10459  $encrypted = substr($font, (12 + $fmetric['size1']), $fmetric['size2']);
10460  $data .= $encrypted;
10461  // store compressed font
10462  $fp = fopen($outpath.$fmetric['file'], 'wb');
10463  fwrite($fp, gzcompress($data));
10464  fclose($fp);
10465  // get font info
10466  $fmetric['Flags'] = $flags;
10467  preg_match ('#/FullName[\s]*\(([^\)]*)#', $font, $matches);
10468  $fmetric['name'] = preg_replace('/[^a-zA-Z0-9_\-]/', '', $matches[1]);
10469  preg_match('#/FontBBox[\s]*{([^}]*)#', $font, $matches);
10470  $fmetric['bbox'] = trim($matches[1]);
10471  $bv = explode(' ', $fmetric['bbox']);
10472  $fmetric['Ascent'] = intval($bv[3]);
10473  $fmetric['Descent'] = intval($bv[1]);
10474  preg_match('#/ItalicAngle[\s]*([0-9\+\-]*)#', $font, $matches);
10475  $fmetric['italicAngle'] = intval($matches[1]);
10476  if ($fmetric['italicAngle'] != 0) {
10477  $fmetric['Flags'] |= 64;
10478  }
10479  preg_match('#/UnderlinePosition[\s]*([0-9\+\-]*)#', $font, $matches);
10480  $fmetric['underlinePosition'] = intval($matches[1]);
10481  preg_match('#/UnderlineThickness[\s]*([0-9\+\-]*)#', $font, $matches);
10482  $fmetric['underlineThickness'] = intval($matches[1]);
10483  preg_match('#/isFixedPitch[\s]*([^\s]*)#', $font, $matches);
10484  if ($matches[1] == 'true') {
10485  $fmetric['Flags'] |= 1;
10486  }
10487  // get internal map
10488  $imap = array();
10489  if (preg_match_all('#dup[\s]([0-9]+)[\s]*/([^\s]*)[\s]put#sU', $font, $fmap, PREG_SET_ORDER) > 0) {
10490  foreach ($fmap as $v) {
10491  $imap[$v[2]] = $v[1];
10492  }
10493  }
10494  // decrypt eexec encrypted part
10495  $r = 55665; // eexec encryption constant
10496  $c1 = 52845;
10497  $c2 = 22719;
10498  $elen = strlen($encrypted);
10499  $eplain = '';
10500  for ($i = 0; $i < $elen; ++$i) {
10501  $chr = ord($encrypted[$i]);
10502  $eplain .= chr($chr ^ ($r >> 8));
10503  $r = ((($chr + $r) * $c1 + $c2) % 65536);
10504  }
10505  if (preg_match('#/ForceBold[\s]*([^\s]*)#', $eplain, $matches) > 0) {
10506  if ($matches[1] == 'true') {
10507  $fmetric['Flags'] |= 0x40000;
10508  }
10509  }
10510  if (preg_match('#/StdVW[\s]*\[([^\]]*)#', $eplain, $matches) > 0) {
10511  $fmetric['StemV'] = intval($matches[1]);
10512  } else {
10513  $fmetric['StemV'] = 70;
10514  }
10515  if (preg_match('#/StdHW[\s]*\[([^\]]*)#', $eplain, $matches) > 0) {
10516  $fmetric['StemH'] = intval($matches[1]);
10517  } else {
10518  $fmetric['StemH'] = 30;
10519  }
10520  if (preg_match('#/BlueValues[\s]*\[([^\]]*)#', $eplain, $matches) > 0) {
10521  $bv = explode(' ', $matches[1]);
10522  if (count($bv) >= 6) {
10523  $v1 = intval($bv[2]);
10524  $v2 = intval($bv[4]);
10525  if ($v1 <= $v2) {
10526  $fmetric['XHeight'] = $v1;
10527  $fmetric['CapHeight'] = $v2;
10528  } else {
10529  $fmetric['XHeight'] = $v2;
10530  $fmetric['CapHeight'] = $v1;
10531  }
10532  } else {
10533  $fmetric['XHeight'] = 450;
10534  $fmetric['CapHeight'] = 700;
10535  }
10536  } else {
10537  $fmetric['XHeight'] = 450;
10538  $fmetric['CapHeight'] = 700;
10539  }
10540  // get the number of random bytes at the beginning of charstrings
10541  if (preg_match('#/lenIV[\s]*([0-9]*)#', $eplain, $matches) > 0) {
10542  $lenIV = intval($matches[1]);
10543  } else {
10544  $lenIV = 4;
10545  }
10546  $fmetric['Leading'] = 0;
10547  // get charstring data
10548  $eplain = substr($eplain, (strpos($eplain, '/CharStrings') + 1));
10549  preg_match_all('#/([A-Za-z0-9\.]*)[\s][0-9]+[\s]RD[\s](.*)[\s]ND#sU', $eplain, $matches, PREG_SET_ORDER);
10550  if (!empty($enc) AND isset($this->encmaps->encmap[$enc])) {
10551  $enc_map = $this->encmaps->encmap[$enc];
10552  } else {
10553  $enc_map = false;
10554  }
10555  $fmetric['cw'] = '';
10556  $fmetric['MaxWidth'] = 0;
10557  $cwidths = array();
10558  foreach ($matches as $k => $v) {
10559  $cid = 0;
10560  if (isset($imap[$v[1]])) {
10561  $cid = $imap[$v[1]];
10562  } elseif ($enc_map !== false) {
10563  $cid = array_search($v[1], $enc_map);
10564  if ($cid === false) {
10565  $cid = 0;
10566  } elseif ($cid > 1000) {
10567  $cid -= 1000;
10568  }
10569  }
10570  // decrypt charstring encrypted part
10571  $r = 4330; // charstring encryption constant
10572  $c1 = 52845;
10573  $c2 = 22719;
10574  $cd = $v[2];
10575  $clen = strlen($cd);
10576  $ccom = array();
10577  for ($i = 0; $i < $clen; ++$i) {
10578  $chr = ord($cd[$i]);
10579  $ccom[] = ($chr ^ ($r >> 8));
10580  $r = ((($chr + $r) * $c1 + $c2) % 65536);
10581  }
10582  // decode numbers
10583  $cdec = array();
10584  $ck = 0;
10585  $i = $lenIV;
10586  while ($i < $clen) {
10587  if ($ccom[$i] < 32) {
10588  $cdec[$ck] = $ccom[$i];
10589  if (($ck > 0) AND ($cdec[$ck] == 13)) {
10590  // hsbw command: update width
10591  $cwidths[$cid] = $cdec[($ck - 1)];
10592  }
10593  ++$i;
10594  } elseif (($ccom[$i] >= 32) AND ($ccom[$i] <= 246)) {
10595  $cdec[$ck] = ($ccom[$i] - 139);
10596  ++$i;
10597  } elseif (($ccom[$i] >= 247) AND ($ccom[$i] <= 250)) {
10598  $cdec[$ck] = ((($ccom[$i] - 247) * 256) + $ccom[($i + 1)] + 108);
10599  $i += 2;
10600  } elseif (($ccom[$i] >= 251) AND ($ccom[$i] <= 254)) {
10601  $cdec[$ck] = ((-($ccom[$i] - 251) * 256) - $ccom[($i + 1)] - 108);
10602  $i += 2;
10603  } elseif ($ccom[$i] == 255) {
10604  $sval = chr($ccom[($i + 1)]).chr($ccom[($i + 2)]).chr($ccom[($i + 3)]).chr($ccom[($i + 4)]);
10605  $vsval = unpack('li', $sval);
10606  $cdec[$ck] = $vsval['i'];
10607  $i += 5;
10608  }
10609  ++$ck;
10610  }
10611  } // end for each matches
10612  $fmetric['MissingWidth'] = $cwidths[0];
10613  $fmetric['MaxWidth'] = $fmetric['MissingWidth'];
10614  $fmetric['AvgWidth'] = 0;
10615  // set chars widths
10616  for ($cid = 0; $cid <= 255; ++$cid) {
10617  if (isset($cwidths[$cid])) {
10618  if ($cwidths[$cid] > $fmetric['MaxWidth']) {
10619  $fmetric['MaxWidth'] = $cwidths[$cid];
10620  }
10621  $fmetric['AvgWidth'] += $cwidths[$cid];
10622  $fmetric['cw'] .= ','.$cid.'=>'.$cwidths[$cid];
10623  } else {
10624  $fmetric['cw'] .= ','.$cid.'=>'.$fmetric['MissingWidth'];
10625  }
10626  }
10627  $fmetric['AvgWidth'] = round($fmetric['AvgWidth'] / count($cwidths));
10628  } else {
10629  // ---------- TRUE TYPE ----------
10630  if ($fmetric['type'] != 'cidfont0') {
10631  // store compressed font
10632  $fp = fopen($outpath.$fmetric['file'], 'wb');
10633  fwrite($fp, gzcompress($font));
10634  fclose($fp);
10635  }
10636  $offset = 0; // offset position of the font data
10637  if ($this->_getULONG($font, $offset) != 0x10000) {
10638  // sfnt version must be 0x00010000 for TrueType version 1.0.
10639  return $font;
10640  }
10641  $offset += 4;
10642  // get number of tables
10643  $numTables = $this->_getUSHORT($font, $offset);
10644  $offset += 2;
10645  // skip searchRange, entrySelector and rangeShift
10646  $offset += 6;
10647  // tables array
10648  $table = array();
10649  // ---------- get tables ----------
10650  for ($i = 0; $i < $numTables; ++$i) {
10651  // get table info
10652  $tag = substr($font, $offset, 4);
10653  $offset += 4;
10654  $table[$tag] = array();
10655  $table[$tag]['checkSum'] = $this->_getULONG($font, $offset);
10656  $offset += 4;
10657  $table[$tag]['offset'] = $this->_getULONG($font, $offset);
10658  $offset += 4;
10659  $table[$tag]['length'] = $this->_getULONG($font, $offset);
10660  $offset += 4;
10661  }
10662  // check magicNumber
10663  $offset = $table['head']['offset'] + 12;
10664  if ($this->_getULONG($font, $offset) != 0x5F0F3CF5) {
10665  // magicNumber must be 0x5F0F3CF5
10666  return $font;
10667  }
10668  $offset += 4;
10669  $offset += 2; // skip flags
10670  // get FUnits
10671  $fmetric['unitsPerEm'] = $this->_getUSHORT($font, $offset);
10672  $offset += 2;
10673  // units ratio constant
10674  $urk = (1000 / $fmetric['unitsPerEm']);
10675  $offset += 16; // skip created, modified
10676  $xMin = round($this->_getFWORD($font, $offset) * $urk);
10677  $offset += 2;
10678  $yMin = round($this->_getFWORD($font, $offset) * $urk);
10679  $offset += 2;
10680  $xMax = round($this->_getFWORD($font, $offset) * $urk);
10681  $offset += 2;
10682  $yMax = round($this->_getFWORD($font, $offset) * $urk);
10683  $offset += 2;
10684  $fmetric['bbox'] = ''.$xMin.' '.$yMin.' '.$xMax.' '.$yMax.'';
10685  $macStyle = $this->_getUSHORT($font, $offset);
10686  $offset += 2;
10687  // PDF font flags
10688  $fmetric['Flags'] = $flags;
10689  if (($macStyle & 2) == 2) {
10690  // italic flag
10691  $fmetric['Flags'] |= 64;
10692  }
10693  // get offset mode (indexToLocFormat : 0 = short, 1 = long)
10694  $offset = $table['head']['offset'] + 50;
10695  $short_offset = ($this->_getSHORT($font, $offset) == 0);
10696  $offset += 2;
10697  // get the offsets to the locations of the glyphs in the font, relative to the beginning of the glyphData table
10698  $indexToLoc = array();
10699  $offset = $table['loca']['offset'];
10700  if ($short_offset) {
10701  // short version
10702  $tot_num_glyphs = ($table['loca']['length'] / 2); // numGlyphs + 1
10703  for ($i = 0; $i < $tot_num_glyphs; ++$i) {
10704  $indexToLoc[$i] = $this->_getUSHORT($font, $offset) * 2;
10705  $offset += 2;
10706  }
10707  } else {
10708  // long version
10709  $tot_num_glyphs = ($table['loca']['length'] / 4); // numGlyphs + 1
10710  for ($i = 0; $i < $tot_num_glyphs; ++$i) {
10711  $indexToLoc[$i] = $this->_getULONG($font, $offset);
10712  $offset += 4;
10713  }
10714  }
10715  // get glyphs indexes of chars from cmap table
10716  $offset = $table['cmap']['offset'] + 2;
10717  $numEncodingTables = $this->_getUSHORT($font, $offset);
10718  $offset += 2;
10719  $encodingTables = array();
10720  for ($i = 0; $i < $numEncodingTables; ++$i) {
10721  $encodingTables[$i]['platformID'] = $this->_getUSHORT($font, $offset);
10722  $offset += 2;
10723  $encodingTables[$i]['encodingID'] = $this->_getUSHORT($font, $offset);
10724  $offset += 2;
10725  $encodingTables[$i]['offset'] = $this->_getULONG($font, $offset);
10726  $offset += 4;
10727  }
10728  // ---------- get os/2 metrics ----------
10729  $offset = $table['OS/2']['offset'];
10730  $offset += 2; // skip version
10731  // xAvgCharWidth
10732  $fmetric['AvgWidth'] = round($this->_getFWORD($font, $offset) * $urk);
10733  $offset += 2;
10734  // usWeightClass
10735  $usWeightClass = round($this->_getUFWORD($font, $offset) * $urk);
10736  // estimate StemV and StemH (400 = usWeightClass for Normal - Regular font)
10737  $fmetric['StemV'] = round((70 * $usWeightClass) / 400);
10738  $fmetric['StemH'] = round((30 * $usWeightClass) / 400);
10739  $offset += 2;
10740  $offset += 2; // usWidthClass
10741  $fsType = $this->_getSHORT($font, $offset);
10742  $offset += 2;
10743  if ($fsType == 2) {
10744  $this->Error('This Font cannot be modified, embedded or exchanged in any manner without first obtaining permission of the legal owner.');
10745  }
10746  // ---------- get font name ----------
10747  $fmetric['name'] = '';
10748  $offset = $table['name']['offset'];
10749  $offset += 2; // skip Format selector (=0).
10750  // Number of NameRecords that follow n.
10751  $numNameRecords = $this->_getUSHORT($font, $offset);
10752  $offset += 2;
10753  // Offset to start of string storage (from start of table).
10754  $stringStorageOffset = $this->_getUSHORT($font, $offset);
10755  $offset += 2;
10756  for ($i = 0; $i < $numNameRecords; ++$i) {
10757  $offset += 6; // skip Platform ID, Platform-specific encoding ID, Language ID.
10758  // Name ID.
10759  $nameID = $this->_getUSHORT($font, $offset);
10760  $offset += 2;
10761  if ($nameID == 6) {
10762  // String length (in bytes).
10763  $stringLength = $this->_getUSHORT($font, $offset);
10764  $offset += 2;
10765  // String offset from start of storage area (in bytes).
10766  $stringOffset = $this->_getUSHORT($font, $offset);
10767  $offset += 2;
10768  $offset = ($table['name']['offset'] + $stringStorageOffset + $stringOffset);
10769  $fmetric['name'] = substr($font, $offset, $stringLength);
10770  $fmetric['name'] = preg_replace('/[^a-zA-Z0-9_\-]/', '', $fmetric['name']);
10771  break;
10772  } else {
10773  $offset += 4; // skip String length, String offset
10774  }
10775  }
10776  if (empty($fmetric['name'])) {
10777  $fmetric['name'] = $font_name;
10778  }
10779  // ---------- get post data ----------
10780  $offset = $table['post']['offset'];
10781  $offset += 4; // skip Format Type
10782  $fmetric['italicAngle'] = $this->_getFIXED($font, $offset);
10783  $offset += 4;
10784  $fmetric['underlinePosition'] = round($this->_getFWORD($font, $offset) * $urk);
10785  $offset += 2;
10786  $fmetric['underlineThickness'] = round($this->_getFWORD($font, $offset) * $urk);
10787  $offset += 2;
10788  $isFixedPitch = ($this->_getULONG($font, $offset) == 0) ? false : true;
10789  $offset += 2;
10790  if ($isFixedPitch) {
10791  $fmetric['Flags'] |= 1;
10792  }
10793  // ---------- get hhea data ----------
10794  $offset = $table['hhea']['offset'];
10795  $offset += 4; // skip Table version number
10796  // Ascender
10797  $fmetric['Ascent'] = round($this->_getFWORD($font, $offset) * $urk);
10798  $offset += 2;
10799  // Descender
10800  $fmetric['Descent'] = round($this->_getFWORD($font, $offset) * $urk);
10801  $offset += 2;
10802  // LineGap
10803  $fmetric['Leading'] = round($this->_getFWORD($font, $offset) * $urk);
10804  $offset += 2;
10805  // advanceWidthMax
10806  $fmetric['MaxWidth'] = round($this->_getUFWORD($font, $offset) * $urk);
10807  $offset += 2;
10808  $offset += 22; // skip some values
10809  // get the number of hMetric entries in hmtx table
10810  $numberOfHMetrics = $this->_getUSHORT($font, $offset);
10811  // ---------- get maxp data ----------
10812  $offset = $table['maxp']['offset'];
10813  $offset += 4; // skip Table version number
10814  // get the the number of glyphs in the font.
10815  $numGlyphs = $this->_getUSHORT($font, $offset);
10816  // ---------- get CIDToGIDMap ----------
10817  $ctg = array();
10818  foreach ($encodingTables as $enctable) {
10819  // get only specified Platform ID and Encoding ID
10820  if (($enctable['platformID'] == $platid) AND ($enctable['encodingID'] == $encid)) {
10821  $offset = $table['cmap']['offset'] + $enctable['offset'];
10822  $format = $this->_getUSHORT($font, $offset);
10823  $offset += 2;
10824  switch ($format) {
10825  case 0: { // Format 0: Byte encoding table
10826  $offset += 4; // skip length and version/language
10827  for ($c = 0; $c < 256; ++$c) {
10828  $g = $this->_getBYTE($font, $offset);
10829  $ctg[$c] = $g;
10830  ++$offset;
10831  }
10832  break;
10833  }
10834  case 2: { // Format 2: High-byte mapping through table
10835  $offset += 4; // skip length and version/language
10836  $numSubHeaders = 0;
10837  for ($i = 0; $i < 256; ++$i) {
10838  // Array that maps high bytes to subHeaders: value is subHeader index * 8.
10839  $subHeaderKeys[$i] = ($this->_getUSHORT($font, $offset) / 8);
10840  $offset += 2;
10841  if ($numSubHeaders < $subHeaderKeys[$i]) {
10842  $numSubHeaders = $subHeaderKeys[$i];
10843  }
10844  }
10845  // the number of subHeaders is equal to the max of subHeaderKeys + 1
10846  ++$numSubHeaders;
10847  // read subHeader structures
10848  $subHeaders = array();
10849  $numGlyphIndexArray = 0;
10850  for ($k = 0; $k < $numSubHeaders; ++$k) {
10851  $subHeaders[$k]['firstCode'] = $this->_getUSHORT($font, $offset);
10852  $offset += 2;
10853  $subHeaders[$k]['entryCount'] = $this->_getUSHORT($font, $offset);
10854  $offset += 2;
10855  $subHeaders[$k]['idDelta'] = $this->_getUSHORT($font, $offset);
10856  $offset += 2;
10857  $subHeaders[$k]['idRangeOffset'] = $this->_getUSHORT($font, $offset);
10858  $offset += 2;
10859  $subHeaders[$k]['idRangeOffset'] -= (2 + (($numSubHeaders - $k - 1) * 8));
10860  $subHeaders[$k]['idRangeOffset'] /= 2;
10861  $numGlyphIndexArray += $subHeaders[$k]['entryCount'];
10862  }
10863  for ($k = 0; $k < $numGlyphIndexArray; ++$k) {
10864  $glyphIndexArray[$k] = $this->_getUSHORT($font, $offset);
10865  $offset += 2;
10866  }
10867  for ($i = 0; $i < 256; ++$i) {
10868  $k = $subHeaderKeys[$i];
10869  if ($k == 0) {
10870  // one byte code
10871  $c = $i;
10872  $g = $glyphIndexArray[0];
10873  $ctg[$c] = $g;
10874  } else {
10875  // two bytes code
10876  $start_byte = $subHeaders[$k]['firstCode'];
10877  $end_byte = $start_byte + $subHeaders[$k]['entryCount'];
10878  for ($j = $start_byte; $j < $end_byte; ++$j) {
10879  // combine high and low bytes
10880  $c = (($i << 8) + $j);
10881  $idRangeOffset = ($subHeaders[$k]['idRangeOffset'] + $j - $subHeaders[$k]['firstCode']);
10882  $g = ($glyphIndexArray[$idRangeOffset] + $idDelta[$k]) % 65536;
10883  if ($g < 0) {
10884  $g = 0;
10885  }
10886  $ctg[$c] = $g;
10887  }
10888  }
10889  }
10890  break;
10891  }
10892  case 4: { // Format 4: Segment mapping to delta values
10893  $length = $this->_getUSHORT($font, $offset);
10894  $offset += 2;
10895  $offset += 2; // skip version/language
10896  $segCount = ($this->_getUSHORT($font, $offset) / 2);
10897  $offset += 2;
10898  $offset += 6; // skip searchRange, entrySelector, rangeShift
10899  $endCount = array(); // array of end character codes for each segment
10900  for ($k = 0; $k < $segCount; ++$k) {
10901  $endCount[$k] = $this->_getUSHORT($font, $offset);
10902  $offset += 2;
10903  }
10904  $offset += 2; // skip reservedPad
10905  $startCount = array(); // array of start character codes for each segment
10906  for ($k = 0; $k < $segCount; ++$k) {
10907  $startCount[$k] = $this->_getUSHORT($font, $offset);
10908  $offset += 2;
10909  }
10910  $idDelta = array(); // delta for all character codes in segment
10911  for ($k = 0; $k < $segCount; ++$k) {
10912  $idDelta[$k] = $this->_getUSHORT($font, $offset);
10913  $offset += 2;
10914  }
10915  $idRangeOffset = array(); // Offsets into glyphIdArray or 0
10916  for ($k = 0; $k < $segCount; ++$k) {
10917  $idRangeOffset[$k] = $this->_getUSHORT($font, $offset);
10918  $offset += 2;
10919  }
10920  $gidlen = ($length / 2) - 8 - (4 * $segCount);
10921  $glyphIdArray = array(); // glyph index array
10922  for ($k = 0; $k < $gidlen; ++$k) {
10923  $glyphIdArray[$k] = $this->_getUSHORT($font, $offset);
10924  $offset += 2;
10925  }
10926  for ($k = 0; $k < $segCount; ++$k) {
10927  for ($c = $startCount[$k]; $c <= $endCount[$k]; ++$c) {
10928  if ($idRangeOffset[$k] == 0) {
10929  $g = ($idDelta[$k] + $c) % 65536;
10930  } else {
10931  $gid = (($idRangeOffset[$k] / 2) + ($c - $startCount[$k]) - ($segCount - $k));
10932  $g = ($glyphIdArray[$gid] + $idDelta[$k]) % 65536;
10933  }
10934  if ($g < 0) {
10935  $g = 0;
10936  }
10937  $ctg[$c] = $g;
10938  }
10939  }
10940  break;
10941  }
10942  case 6: { // Format 6: Trimmed table mapping
10943  $offset += 4; // skip length and version/language
10944  $firstCode = $this->_getUSHORT($font, $offset);
10945  $offset += 2;
10946  $entryCount = $this->_getUSHORT($font, $offset);
10947  $offset += 2;
10948  for ($k = 0; $k < $entryCount; ++$k) {
10949  $c = ($k + $firstCode);
10950  $g = $this->_getUSHORT($font, $offset);
10951  $offset += 2;
10952  $ctg[$c] = $g;
10953  }
10954  break;
10955  }
10956  case 8: { // Format 8: Mixed 16-bit and 32-bit coverage
10957  $offset += 10; // skip reserved, length and version/language
10958  for ($k = 0; $k < 8192; ++$k) {
10959  $is32[$k] = $this->_getBYTE($font, $offset);
10960  ++$offset;
10961  }
10962  $nGroups = $this->_getULONG($font, $offset);
10963  $offset += 4;
10964  for ($i = 0; $i < $nGroups; ++$i) {
10965  $startCharCode = $this->_getULONG($font, $offset);
10966  $offset += 4;
10967  $endCharCode = $this->_getULONG($font, $offset);
10968  $offset += 4;
10969  $startGlyphID = $this->_getULONG($font, $offset);
10970  $offset += 4;
10971  for ($k = $startCharCode; $k <= $endCharCode; ++$k) {
10972  $is32idx = floor($c / 8);
10973  if ((isset($is32[$is32idx])) AND (($is32[$is32idx] & (1 << (7 - ($c % 8)))) == 0)) {
10974  $c = $k;
10975  } else {
10976  // 32 bit format
10977  // convert to decimal (http://www.unicode.org/faq//utf_bom.html#utf16-4)
10978  //LEAD_OFFSET = (0xD800 - (0x10000 >> 10)) = 55232
10979  //SURROGATE_OFFSET = (0x10000 - (0xD800 << 10) - 0xDC00) = -56613888
10980  $c = ((55232 + ($k >> 10)) << 10) + (0xDC00 + ($k & 0x3FF)) -56613888;
10981  }
10982  $ctg[$c] = 0;
10983  ++$startGlyphID;
10984  }
10985  }
10986  break;
10987  }
10988  case 10: { // Format 10: Trimmed array
10989  $offset += 10; // skip reserved, length and version/language
10990  $startCharCode = $this->_getULONG($font, $offset);
10991  $offset += 4;
10992  $numChars = $this->_getULONG($font, $offset);
10993  $offset += 4;
10994  for ($k = 0; $k < $numChars; ++$k) {
10995  $c = ($k + $startCharCode);
10996  $g = $this->_getUSHORT($font, $offset);
10997  $ctg[$c] = $g;
10998  $offset += 2;
10999  }
11000  break;
11001  }
11002  case 12: { // Format 12: Segmented coverage
11003  $offset += 10; // skip length and version/language
11004  $nGroups = $this->_getULONG($font, $offset);
11005  $offset += 4;
11006  for ($k = 0; $k < $nGroups; ++$k) {
11007  $startCharCode = $this->_getULONG($font, $offset);
11008  $offset += 4;
11009  $endCharCode = $this->_getULONG($font, $offset);
11010  $offset += 4;
11011  $startGlyphCode = $this->_getULONG($font, $offset);
11012  $offset += 4;
11013  for ($c = $startCharCode; $c <= $endCharCode; ++$c) {
11014  $ctg[$c] = $startGlyphCode;
11015  ++$startGlyphCode;
11016  }
11017  }
11018  break;
11019  }
11020  case 13: { // Format 13: Many-to-one range mappings
11021  // to be implemented ...
11022  break;
11023  }
11024  case 14: { // Format 14: Unicode Variation Sequences
11025  // to be implemented ...
11026  break;
11027  }
11028  }
11029  }
11030  }
11031  if (!isset($ctg[0])) {
11032  $ctg[0] = 0;
11033  }
11034  // get xHeight (height of x)
11035  $offset = ($table['glyf']['offset'] + $indexToLoc[$ctg[120]] + 4);
11036  $yMin = $this->_getFWORD($font, $offset);
11037  $offset += 4;
11038  $yMax = $this->_getFWORD($font, $offset);
11039  $offset += 2;
11040  $fmetric['XHeight'] = round(($yMax - $yMin) * $urk);
11041  // get CapHeight (height of H)
11042  $offset = ($table['glyf']['offset'] + $indexToLoc[$ctg[72]] + 4);
11043  $yMin = $this->_getFWORD($font, $offset);
11044  $offset += 4;
11045  $yMax = $this->_getFWORD($font, $offset);
11046  $offset += 2;
11047  $fmetric['CapHeight'] = round(($yMax - $yMin) * $urk);
11048  // ceate widths array
11049  $cw = array();
11050  $offset = $table['hmtx']['offset'];
11051  for ($i = 0 ; $i < $numberOfHMetrics; ++$i) {
11052  $cw[$i] = round($this->_getUFWORD($font, $offset) * $urk);
11053  $offset += 4; // skip lsb
11054  }
11055  if ($numberOfHMetrics < $numGlyphs) {
11056  // fill missing widths with the last value
11057  $cw = array_pad($cw, $numGlyphs, $cw[($numberOfHMetrics - 1)]);
11058  }
11059  $fmetric['MissingWidth'] = $cw[0];
11060  $fmetric['cw'] = '';
11061  for ($cid = 0; $cid <= 65535; ++$cid) {
11062  if (isset($ctg[$cid])) {
11063  if (isset($cw[$ctg[$cid]])) {
11064  $fmetric['cw'] .= ','.$cid.'=>'.$cw[$ctg[$cid]];
11065  }
11066  if ($addcbbox AND isset($indexToLoc[$ctg[$cid]])) {
11067  $offset = ($table['glyf']['offset'] + $indexToLoc[$ctg[$cid]]);
11068  $xMin = round($this->_getFWORD($font, $offset + 2)) * $urk;
11069  $yMin = round($this->_getFWORD($font, $offset + 4)) * $urk;
11070  $xMax = round($this->_getFWORD($font, $offset + 6)) * $urk;
11071  $yMax = round($this->_getFWORD($font, $offset + 8)) * $urk;
11072  $fmetric['cbbox'] .= ','.$cid.'=>array('.$xMin.','.$yMin.','.$xMax.','.$yMax.')';
11073  }
11074  }
11075  }
11076  } // end of true type
11077  if (($fmetric['type'] == 'TrueTypeUnicode') AND (count($ctg) == 256)) {
11078  $fmetric['type'] == 'TrueType';
11079  }
11080  // ---------- create php font file ----------
11081  $pfile = '<'.'?'.'php'."\n";
11082  $pfile .= '// TCPDF FONT FILE DESCRIPTION'."\n";
11083  $pfile .= '$type=\''.$fmetric['type'].'\';'."\n";
11084  $pfile .= '$name=\''.$fmetric['name'].'\';'."\n";
11085  $pfile .= '$up='.$fmetric['underlinePosition'].';'."\n";
11086  $pfile .= '$ut='.$fmetric['underlineThickness'].';'."\n";
11087  if ($fmetric['MissingWidth'] > 0) {
11088  $pfile .= '$dw='.$fmetric['MissingWidth'].';'."\n";
11089  } else {
11090  $pfile .= '$dw='.$fmetric['AvgWidth'].';'."\n";
11091  }
11092  $pfile .= '$diff=\''.$fmetric['diff'].'\';'."\n";
11093  if ($fmetric['type'] == 'Type1') {
11094  // Type 1
11095  $pfile .= '$enc=\''.$fmetric['enc'].'\';'."\n";
11096  $pfile .= '$file=\''.$fmetric['file'].'\';'."\n";
11097  $pfile .= '$size1='.$fmetric['size1'].';'."\n";
11098  $pfile .= '$size2='.$fmetric['size2'].';'."\n";
11099  } else {
11100  $pfile .= '$originalsize='.$fmetric['originalsize'].';'."\n";
11101  if ($fmetric['type'] == 'cidfont0') {
11102  // CID-0
11103  switch ($fonttype) {
11104  case 'CID0JP': {
11105  $pfile .= '// Japanese'."\n";
11106  $pfile .= '$enc=\'UniJIS-UTF16-H\';'."\n";
11107  $pfile .= '$cidinfo=array(\'Registry\'=>\'Adobe\', \'Ordering\'=>\'Japan1\',\'Supplement\'=>5);'."\n";
11108  $pfile .= 'include(dirname(__FILE__).\'/uni2cid_aj16.php\');'."\n";
11109  break;
11110  }
11111  case 'CID0KR': {
11112  $pfile .= '// Korean'."\n";
11113  $pfile .= '$enc=\'UniKS-UTF16-H\';'."\n";
11114  $pfile .= '$cidinfo=array(\'Registry\'=>\'Adobe\', \'Ordering\'=>\'Korea1\',\'Supplement\'=>0);'."\n";
11115  $pfile .= 'include(dirname(__FILE__).\'/uni2cid_ak12.php\');'."\n";
11116  break;
11117  }
11118  case 'CID0CS': {
11119  $pfile .= '// Chinese Simplified'."\n";
11120  $pfile .= '$enc=\'UniGB-UTF16-H\';'."\n";
11121  $pfile .= '$cidinfo=array(\'Registry\'=>\'Adobe\', \'Ordering\'=>\'GB1\',\'Supplement\'=>2);'."\n";
11122  $pfile .= 'include(dirname(__FILE__).\'/uni2cid_ag15.php\');'."\n";
11123  break;
11124  }
11125  case 'CID0CT':
11126  default: {
11127  $pfile .= '// Chinese Traditional'."\n";
11128  $pfile .= '$enc=\'UniCNS-UTF16-H\';'."\n";
11129  $pfile .= '$cidinfo=array(\'Registry\'=>\'Adobe\', \'Ordering\'=>\'CNS1\',\'Supplement\'=>0);'."\n";
11130  $pfile .= 'include(dirname(__FILE__).\'/uni2cid_aj16.php\');'."\n";
11131  break;
11132  }
11133  }
11134  } else {
11135  // TrueType
11136  $pfile .= '$enc=\''.$fmetric['enc'].'\';'."\n";
11137  $pfile .= '$file=\''.$fmetric['file'].'\';'."\n";
11138  $pfile .= '$ctg=\''.$fmetric['ctg'].'\';'."\n";
11139  // create CIDToGIDMap
11140  $cidtogidmap = str_pad('', 131072, "\x00"); // (256 * 256 * 2) = 131072
11141  foreach ($ctg as $cid => $gid) {
11142  $cidtogidmap = $this->updateCIDtoGIDmap($cidtogidmap, $cid, $ctg[$cid]);
11143  }
11144  // store compressed CIDToGIDMap
11145  $fp = fopen($outpath.$fmetric['ctg'], 'wb');
11146  fwrite($fp, gzcompress($cidtogidmap));
11147  fclose($fp);
11148  }
11149  }
11150  $pfile .= '$desc=array(';
11151  $pfile .= '\'Flags\'=>'.$fmetric['Flags'].',';
11152  $pfile .= '\'FontBBox\'=>\'['.$fmetric['bbox'].']\',';
11153  $pfile .= '\'ItalicAngle\'=>'.$fmetric['italicAngle'].',';
11154  $pfile .= '\'Ascent\'=>'.$fmetric['Ascent'].',';
11155  $pfile .= '\'Descent\'=>'.$fmetric['Descent'].',';
11156  $pfile .= '\'Leading\'=>'.$fmetric['Leading'].',';
11157  $pfile .= '\'CapHeight\'=>'.$fmetric['CapHeight'].',';
11158  $pfile .= '\'XHeight\'=>'.$fmetric['XHeight'].',';
11159  $pfile .= '\'StemV\'=>'.$fmetric['StemV'].',';
11160  $pfile .= '\'StemH\'=>'.$fmetric['StemH'].',';
11161  $pfile .= '\'AvgWidth\'=>'.$fmetric['AvgWidth'].',';
11162  $pfile .= '\'MaxWidth\'=>'.$fmetric['MaxWidth'].',';
11163  $pfile .= '\'MissingWidth\'=>'.$fmetric['MissingWidth'].'';
11164  $pfile .= ');'."\n";
11165  if (isset($fmetric['cbbox'])) {
11166  $pfile .= '$cbbox=array('.substr($fmetric['cbbox'], 1).');'."\n";
11167  }
11168  $pfile .= '$cw=array('.substr($fmetric['cw'], 1).');'."\n";
11169  $pfile .= '// --- EOF ---'."\n";
11170  // store file
11171  $fp = fopen($outpath.$font_name.'.php', 'w');
11172  fwrite($fp, $pfile);
11173  fclose($fp);
11174  // return TCPDF font name
11175  return $font_name;
11176  }
11177 
11187  protected function _getTrueTypeFontSubset($font, $subsetchars) {
11188  ksort($subsetchars);
11189  $offset = 0; // offset position of the font data
11190  if ($this->_getULONG($font, $offset) != 0x10000) {
11191  // sfnt version must be 0x00010000 for TrueType version 1.0.
11192  return $font;
11193  }
11194  $offset += 4;
11195  // get number of tables
11196  $numTables = $this->_getUSHORT($font, $offset);
11197  $offset += 2;
11198  // skip searchRange, entrySelector and rangeShift
11199  $offset += 6;
11200  // tables array
11201  $table = array();
11202  // for each table
11203  for ($i = 0; $i < $numTables; ++$i) {
11204  // get table info
11205  $tag = substr($font, $offset, 4);
11206  $offset += 4;
11207  $table[$tag] = array();
11208  $table[$tag]['checkSum'] = $this->_getULONG($font, $offset);
11209  $offset += 4;
11210  $table[$tag]['offset'] = $this->_getULONG($font, $offset);
11211  $offset += 4;
11212  $table[$tag]['length'] = $this->_getULONG($font, $offset);
11213  $offset += 4;
11214  }
11215  // check magicNumber
11216  $offset = $table['head']['offset'] + 12;
11217  if ($this->_getULONG($font, $offset) != 0x5F0F3CF5) {
11218  // magicNumber must be 0x5F0F3CF5
11219  return $font;
11220  }
11221  $offset += 4;
11222  // get offset mode (indexToLocFormat : 0 = short, 1 = long)
11223  $offset = $table['head']['offset'] + 50;
11224  $short_offset = ($this->_getSHORT($font, $offset) == 0);
11225  $offset += 2;
11226  // get the offsets to the locations of the glyphs in the font, relative to the beginning of the glyphData table
11227  $indexToLoc = array();
11228  $offset = $table['loca']['offset'];
11229  if ($short_offset) {
11230  // short version
11231  $tot_num_glyphs = ($table['loca']['length'] / 2); // numGlyphs + 1
11232  for ($i = 0; $i < $tot_num_glyphs; ++$i) {
11233  $indexToLoc[$i] = $this->_getUSHORT($font, $offset) * 2;
11234  $offset += 2;
11235  }
11236  } else {
11237  // long version
11238  $tot_num_glyphs = ($table['loca']['length'] / 4); // numGlyphs + 1
11239  for ($i = 0; $i < $tot_num_glyphs; ++$i) {
11240  $indexToLoc[$i] = $this->_getULONG($font, $offset);
11241  $offset += 4;
11242  }
11243  }
11244  // get glyphs indexes of chars from cmap table
11245  $subsetglyphs = array(); // glyph IDs on key
11246  $subsetglyphs[0] = true; // character codes that do not correspond to any glyph in the font should be mapped to glyph index 0
11247  $offset = $table['cmap']['offset'] + 2;
11248  $numEncodingTables = $this->_getUSHORT($font, $offset);
11249  $offset += 2;
11250  $encodingTables = array();
11251  for ($i = 0; $i < $numEncodingTables; ++$i) {
11252  $encodingTables[$i]['platformID'] = $this->_getUSHORT($font, $offset);
11253  $offset += 2;
11254  $encodingTables[$i]['encodingID'] = $this->_getUSHORT($font, $offset);
11255  $offset += 2;
11256  $encodingTables[$i]['offset'] = $this->_getULONG($font, $offset);
11257  $offset += 4;
11258  }
11259  foreach ($encodingTables as $enctable) {
11260  // get all platforms and encodings
11261  $offset = $table['cmap']['offset'] + $enctable['offset'];
11262  $format = $this->_getUSHORT($font, $offset);
11263  $offset += 2;
11264  switch ($format) {
11265  case 0: { // Format 0: Byte encoding table
11266  $offset += 4; // skip length and version/language
11267  for ($c = 0; $c < 256; ++$c) {
11268  if (isset($subsetchars[$c])) {
11269  $g = $this->_getBYTE($font, $offset);
11270  $subsetglyphs[$g] = true;
11271  }
11272  ++$offset;
11273  }
11274  break;
11275  }
11276  case 2: { // Format 2: High-byte mapping through table
11277  $offset += 4; // skip length and version/language
11278  $numSubHeaders = 0;
11279  for ($i = 0; $i < 256; ++$i) {
11280  // Array that maps high bytes to subHeaders: value is subHeader index * 8.
11281  $subHeaderKeys[$i] = ($this->_getUSHORT($font, $offset) / 8);
11282  $offset += 2;
11283  if ($numSubHeaders < $subHeaderKeys[$i]) {
11284  $numSubHeaders = $subHeaderKeys[$i];
11285  }
11286  }
11287  // the number of subHeaders is equal to the max of subHeaderKeys + 1
11288  ++$numSubHeaders;
11289  // read subHeader structures
11290  $subHeaders = array();
11291  $numGlyphIndexArray = 0;
11292  for ($k = 0; $k < $numSubHeaders; ++$k) {
11293  $subHeaders[$k]['firstCode'] = $this->_getUSHORT($font, $offset);
11294  $offset += 2;
11295  $subHeaders[$k]['entryCount'] = $this->_getUSHORT($font, $offset);
11296  $offset += 2;
11297  $subHeaders[$k]['idDelta'] = $this->_getUSHORT($font, $offset);
11298  $offset += 2;
11299  $subHeaders[$k]['idRangeOffset'] = $this->_getUSHORT($font, $offset);
11300  $offset += 2;
11301  $subHeaders[$k]['idRangeOffset'] -= (2 + (($numSubHeaders - $k - 1) * 8));
11302  $subHeaders[$k]['idRangeOffset'] /= 2;
11303  $numGlyphIndexArray += $subHeaders[$k]['entryCount'];
11304  }
11305  for ($k = 0; $k < $numGlyphIndexArray; ++$k) {
11306  $glyphIndexArray[$k] = $this->_getUSHORT($font, $offset);
11307  $offset += 2;
11308  }
11309  for ($i = 0; $i < 256; ++$i) {
11310  $k = $subHeaderKeys[$i];
11311  if ($k == 0) {
11312  // one byte code
11313  $c = $i;
11314  if (isset($subsetchars[$c])) {
11315  $g = $glyphIndexArray[0];
11316  $subsetglyphs[$g] = true;
11317  }
11318  } else {
11319  // two bytes code
11320  $start_byte = $subHeaders[$k]['firstCode'];
11321  $end_byte = $start_byte + $subHeaders[$k]['entryCount'];
11322  for ($j = $start_byte; $j < $end_byte; ++$j) {
11323  // combine high and low bytes
11324  $c = (($i << 8) + $j);
11325  if (isset($subsetchars[$c])) {
11326  $idRangeOffset = ($subHeaders[$k]['idRangeOffset'] + $j - $subHeaders[$k]['firstCode']);
11327  $g = ($glyphIndexArray[$idRangeOffset] + $idDelta[$k]) % 65536;
11328  if ($g < 0) {
11329  $g = 0;
11330  }
11331  $subsetglyphs[$g] = true;
11332  }
11333  }
11334  }
11335  }
11336  break;
11337  }
11338  case 4: { // Format 4: Segment mapping to delta values
11339  $length = $this->_getUSHORT($font, $offset);
11340  $offset += 2;
11341  $offset += 2; // skip version/language
11342  $segCount = ($this->_getUSHORT($font, $offset) / 2);
11343  $offset += 2;
11344  $offset += 6; // skip searchRange, entrySelector, rangeShift
11345  $endCount = array(); // array of end character codes for each segment
11346  for ($k = 0; $k < $segCount; ++$k) {
11347  $endCount[$k] = $this->_getUSHORT($font, $offset);
11348  $offset += 2;
11349  }
11350  $offset += 2; // skip reservedPad
11351  $startCount = array(); // array of start character codes for each segment
11352  for ($k = 0; $k < $segCount; ++$k) {
11353  $startCount[$k] = $this->_getUSHORT($font, $offset);
11354  $offset += 2;
11355  }
11356  $idDelta = array(); // delta for all character codes in segment
11357  for ($k = 0; $k < $segCount; ++$k) {
11358  $idDelta[$k] = $this->_getUSHORT($font, $offset);
11359  $offset += 2;
11360  }
11361  $idRangeOffset = array(); // Offsets into glyphIdArray or 0
11362  for ($k = 0; $k < $segCount; ++$k) {
11363  $idRangeOffset[$k] = $this->_getUSHORT($font, $offset);
11364  $offset += 2;
11365  }
11366  $gidlen = ($length / 2) - 8 - (4 * $segCount);
11367  $glyphIdArray = array(); // glyph index array
11368  for ($k = 0; $k < $gidlen; ++$k) {
11369  $glyphIdArray[$k] = $this->_getUSHORT($font, $offset);
11370  $offset += 2;
11371  }
11372  for ($k = 0; $k < $segCount; ++$k) {
11373  for ($c = $startCount[$k]; $c <= $endCount[$k]; ++$c) {
11374  if (isset($subsetchars[$c])) {
11375  if ($idRangeOffset[$k] == 0) {
11376  $g = ($idDelta[$k] + $c) % 65536;
11377  } else {
11378  $gid = (($idRangeOffset[$k] / 2) + ($c - $startCount[$k]) - ($segCount - $k));
11379  $g = ($glyphIdArray[$gid] + $idDelta[$k]) % 65536;
11380  }
11381  if ($g < 0) {
11382  $g = 0;
11383  }
11384  $subsetglyphs[$g] = true;
11385  }
11386  }
11387  }
11388  break;
11389  }
11390  case 6: { // Format 6: Trimmed table mapping
11391  $offset += 4; // skip length and version/language
11392  $firstCode = $this->_getUSHORT($font, $offset);
11393  $offset += 2;
11394  $entryCount = $this->_getUSHORT($font, $offset);
11395  $offset += 2;
11396  for ($k = 0; $k < $entryCount; ++$k) {
11397  $c = ($k + $firstCode);
11398  if (isset($subsetchars[$c])) {
11399  $g = $this->_getUSHORT($font, $offset);
11400  $subsetglyphs[$g] = true;
11401  }
11402  $offset += 2;
11403  }
11404  break;
11405  }
11406  case 8: { // Format 8: Mixed 16-bit and 32-bit coverage
11407  $offset += 10; // skip reserved, length and version/language
11408  for ($k = 0; $k < 8192; ++$k) {
11409  $is32[$k] = $this->_getBYTE($font, $offset);
11410  ++$offset;
11411  }
11412  $nGroups = $this->_getULONG($font, $offset);
11413  $offset += 4;
11414  for ($i = 0; $i < $nGroups; ++$i) {
11415  $startCharCode = $this->_getULONG($font, $offset);
11416  $offset += 4;
11417  $endCharCode = $this->_getULONG($font, $offset);
11418  $offset += 4;
11419  $startGlyphID = $this->_getULONG($font, $offset);
11420  $offset += 4;
11421  for ($k = $startCharCode; $k <= $endCharCode; ++$k) {
11422  $is32idx = floor($c / 8);
11423  if ((isset($is32[$is32idx])) AND (($is32[$is32idx] & (1 << (7 - ($c % 8)))) == 0)) {
11424  $c = $k;
11425  } else {
11426  // 32 bit format
11427  // convert to decimal (http://www.unicode.org/faq//utf_bom.html#utf16-4)
11428  //LEAD_OFFSET = (0xD800 - (0x10000 >> 10)) = 55232
11429  //SURROGATE_OFFSET = (0x10000 - (0xD800 << 10) - 0xDC00) = -56613888
11430  $c = ((55232 + ($k >> 10)) << 10) + (0xDC00 + ($k & 0x3FF)) -56613888;
11431  }
11432  if (isset($subsetchars[$c])) {
11433  $subsetglyphs[$startGlyphID] = true;
11434  }
11435  ++$startGlyphID;
11436  }
11437  }
11438  break;
11439  }
11440  case 10: { // Format 10: Trimmed array
11441  $offset += 10; // skip reserved, length and version/language
11442  $startCharCode = $this->_getULONG($font, $offset);
11443  $offset += 4;
11444  $numChars = $this->_getULONG($font, $offset);
11445  $offset += 4;
11446  for ($k = 0; $k < $numChars; ++$k) {
11447  $c = ($k + $startCharCode);
11448  if (isset($subsetchars[$c])) {
11449  $g = $this->_getUSHORT($font, $offset);
11450  $subsetglyphs[$g] = true;
11451  }
11452  $offset += 2;
11453  }
11454  break;
11455  }
11456  case 12: { // Format 12: Segmented coverage
11457  $offset += 10; // skip length and version/language
11458  $nGroups = $this->_getULONG($font, $offset);
11459  $offset += 4;
11460  for ($k = 0; $k < $nGroups; ++$k) {
11461  $startCharCode = $this->_getULONG($font, $offset);
11462  $offset += 4;
11463  $endCharCode = $this->_getULONG($font, $offset);
11464  $offset += 4;
11465  $startGlyphCode = $this->_getULONG($font, $offset);
11466  $offset += 4;
11467  for ($c = $startCharCode; $c <= $endCharCode; ++$c) {
11468  if (isset($subsetchars[$c])) {
11469  $subsetglyphs[$startGlyphCode] = true;
11470  }
11471  ++$startGlyphCode;
11472  }
11473  }
11474  break;
11475  }
11476  case 13: { // Format 13: Many-to-one range mappings
11477  // to be implemented ...
11478  break;
11479  }
11480  case 14: { // Format 14: Unicode Variation Sequences
11481  // to be implemented ...
11482  break;
11483  }
11484  }
11485  }
11486  // include all parts of composite glyphs
11487  $new_sga = $subsetglyphs;
11488  while (!empty($new_sga)) {
11489  $sga = $new_sga;
11490  $new_sga = array();
11491  foreach ($sga as $key => $val) {
11492  if (isset($indexToLoc[$key])) {
11493  $offset = ($table['glyf']['offset'] + $indexToLoc[$key]);
11494  $numberOfContours = $this->_getSHORT($font, $offset);
11495  $offset += 2;
11496  if ($numberOfContours < 0) { // composite glyph
11497  $offset += 8; // skip xMin, yMin, xMax, yMax
11498  do {
11499  $flags = $this->_getUSHORT($font, $offset);
11500  $offset += 2;
11501  $glyphIndex = $this->_getUSHORT($font, $offset);
11502  $offset += 2;
11503  if (!isset($subsetglyphs[$glyphIndex])) {
11504  // add missing glyphs
11505  $new_sga[$glyphIndex] = true;
11506  }
11507  // skip some bytes by case
11508  if ($flags & 1) {
11509  $offset += 4;
11510  } else {
11511  $offset += 2;
11512  }
11513  if ($flags & 8) {
11514  $offset += 2;
11515  } elseif ($flags & 64) {
11516  $offset += 4;
11517  } elseif ($flags & 128) {
11518  $offset += 8;
11519  }
11520  } while ($flags & 32);
11521  }
11522  }
11523  }
11524  $subsetglyphs += $new_sga;
11525  }
11526  // sort glyphs by key (and remove duplicates)
11527  ksort($subsetglyphs);
11528  // build new glyf and loca tables
11529  $glyf = '';
11530  $loca = '';
11531  $offset = 0;
11532  $glyf_offset = $table['glyf']['offset'];
11533  for ($i = 0; $i < $tot_num_glyphs; ++$i) {
11534  if (isset($subsetglyphs[$i])) {
11535  $length = ($indexToLoc[($i + 1)] - $indexToLoc[$i]);
11536  $glyf .= substr($font, ($glyf_offset + $indexToLoc[$i]), $length);
11537  } else {
11538  $length = 0;
11539  }
11540  if ($short_offset) {
11541  $loca .= pack('n', ($offset / 2));
11542  } else {
11543  $loca .= pack('N', $offset);
11544  }
11545  $offset += $length;
11546  }
11547  // array of table names to preserve (loca and glyf tables will be added later)
11548  // the cmap table is not needed and shall not be present, since the mapping from character codes to glyph descriptions is provided separately
11549  $table_names = array ('head', 'hhea', 'hmtx', 'maxp', 'cvt ', 'fpgm', 'prep'); // minimum required table names
11550  // get the tables to preserve
11551  $offset = 12;
11552  foreach ($table as $tag => $val) {
11553  if (in_array($tag, $table_names)) {
11554  $table[$tag]['data'] = substr($font, $table[$tag]['offset'], $table[$tag]['length']);
11555  if ($tag == 'head') {
11556  // set the checkSumAdjustment to 0
11557  $table[$tag]['data'] = substr($table[$tag]['data'], 0, 8)."\x0\x0\x0\x0".substr($table[$tag]['data'], 12);
11558  }
11559  $pad = 4 - ($table[$tag]['length'] % 4);
11560  if ($pad != 4) {
11561  // the length of a table must be a multiple of four bytes
11562  $table[$tag]['length'] += $pad;
11563  $table[$tag]['data'] .= str_repeat("\x0", $pad);
11564  }
11565  $table[$tag]['offset'] = $offset;
11566  $offset += $table[$tag]['length'];
11567  // check sum is not changed (so keep the following line commented)
11568  //$table[$tag]['checkSum'] = $this->_getTTFtableChecksum($table[$tag]['data'], $table[$tag]['length']);
11569  } else {
11570  unset($table[$tag]);
11571  }
11572  }
11573  // add loca
11574  $table['loca']['data'] = $loca;
11575  $table['loca']['length'] = strlen($loca);
11576  $pad = 4 - ($table['loca']['length'] % 4);
11577  if ($pad != 4) {
11578  // the length of a table must be a multiple of four bytes
11579  $table['loca']['length'] += $pad;
11580  $table['loca']['data'] .= str_repeat("\x0", $pad);
11581  }
11582  $table['loca']['offset'] = $offset;
11583  $table['loca']['checkSum'] = $this->_getTTFtableChecksum($table['loca']['data'], $table['loca']['length']);
11584  $offset += $table['loca']['length'];
11585  // add glyf
11586  $table['glyf']['data'] = $glyf;
11587  $table['glyf']['length'] = strlen($glyf);
11588  $pad = 4 - ($table['glyf']['length'] % 4);
11589  if ($pad != 4) {
11590  // the length of a table must be a multiple of four bytes
11591  $table['glyf']['length'] += $pad;
11592  $table['glyf']['data'] .= str_repeat("\x0", $pad);
11593  }
11594  $table['glyf']['offset'] = $offset;
11595  $table['glyf']['checkSum'] = $this->_getTTFtableChecksum($table['glyf']['data'], $table['glyf']['length']);
11596  // rebuild font
11597  $font = '';
11598  $font .= pack('N', 0x10000); // sfnt version
11599  $numTables = count($table);
11600  $font .= pack('n', $numTables); // numTables
11601  $entrySelector = floor(log($numTables, 2));
11602  $searchRange = pow(2, $entrySelector) * 16;
11603  $rangeShift = ($numTables * 16) - $searchRange;
11604  $font .= pack('n', $searchRange); // searchRange
11605  $font .= pack('n', $entrySelector); // entrySelector
11606  $font .= pack('n', $rangeShift); // rangeShift
11607  $offset = ($numTables * 16);
11608  foreach ($table as $tag => $data) {
11609  $font .= $tag; // tag
11610  $font .= pack('N', $data['checkSum']); // checkSum
11611  $font .= pack('N', ($data['offset'] + $offset)); // offset
11612  $font .= pack('N', $data['length']); // length
11613  }
11614  foreach ($table as $data) {
11615  $font .= $data['data'];
11616  }
11617  // set checkSumAdjustment on head table
11618  $checkSumAdjustment = 0xB1B0AFBA - $this->_getTTFtableChecksum($font, strlen($font));
11619  $font = substr($font, 0, $table['head']['offset'] + 8).pack('N', $checkSumAdjustment).substr($font, $table['head']['offset'] + 12);
11620  return $font;
11621  }
11622 
11632  protected function _getTTFtableChecksum($table, $length) {
11633  $sum = 0;
11634  $tlen = ($length / 4);
11635  $offset = 0;
11636  for ($i = 0; $i < $tlen; ++$i) {
11637  $v = unpack('Ni', substr($table, $offset, 4));
11638  $sum += $v['i'];
11639  $offset += 4;
11640  }
11641  $sum = unpack('Ni', pack('N', $sum));
11642  return $sum['i'];
11643  }
11644 
11654  protected function _putfontwidths($font, $cidoffset=0) {
11655  ksort($font['cw']);
11656  $rangeid = 0;
11657  $range = array();
11658  $prevcid = -2;
11659  $prevwidth = -1;
11660  $interval = false;
11661  // for each character
11662  foreach ($font['cw'] as $cid => $width) {
11663  $cid -= $cidoffset;
11664  if ($font['subset'] AND (!isset($font['subsetchars'][$cid]))) {
11665  // ignore the unused characters (font subsetting)
11666  continue;
11667  }
11668  if ($width != $font['dw']) {
11669  if ($cid == ($prevcid + 1)) {
11670  // consecutive CID
11671  if ($width == $prevwidth) {
11672  if ($width == $range[$rangeid][0]) {
11673  $range[$rangeid][] = $width;
11674  } else {
11675  array_pop($range[$rangeid]);
11676  // new range
11677  $rangeid = $prevcid;
11678  $range[$rangeid] = array();
11679  $range[$rangeid][] = $prevwidth;
11680  $range[$rangeid][] = $width;
11681  }
11682  $interval = true;
11683  $range[$rangeid]['interval'] = true;
11684  } else {
11685  if ($interval) {
11686  // new range
11687  $rangeid = $cid;
11688  $range[$rangeid] = array();
11689  $range[$rangeid][] = $width;
11690  } else {
11691  $range[$rangeid][] = $width;
11692  }
11693  $interval = false;
11694  }
11695  } else {
11696  // new range
11697  $rangeid = $cid;
11698  $range[$rangeid] = array();
11699  $range[$rangeid][] = $width;
11700  $interval = false;
11701  }
11702  $prevcid = $cid;
11703  $prevwidth = $width;
11704  }
11705  }
11706  // optimize ranges
11707  $prevk = -1;
11708  $nextk = -1;
11709  $prevint = false;
11710  foreach ($range as $k => $ws) {
11711  $cws = count($ws);
11712  if (($k == $nextk) AND (!$prevint) AND ((!isset($ws['interval'])) OR ($cws < 4))) {
11713  if (isset($range[$k]['interval'])) {
11714  unset($range[$k]['interval']);
11715  }
11716  $range[$prevk] = array_merge($range[$prevk], $range[$k]);
11717  unset($range[$k]);
11718  } else {
11719  $prevk = $k;
11720  }
11721  $nextk = $k + $cws;
11722  if (isset($ws['interval'])) {
11723  if ($cws > 3) {
11724  $prevint = true;
11725  } else {
11726  $prevint = false;
11727  }
11728  if (isset($range[$k]['interval'])) {
11729  unset($range[$k]['interval']);
11730  }
11731  --$nextk;
11732  } else {
11733  $prevint = false;
11734  }
11735  }
11736  // output data
11737  $w = '';
11738  foreach ($range as $k => $ws) {
11739  if (count(array_count_values($ws)) == 1) {
11740  // interval mode is more compact
11741  $w .= ' '.$k.' '.($k + count($ws) - 1).' '.$ws[0];
11742  } else {
11743  // range mode
11744  $w .= ' '.$k.' [ '.implode(' ', $ws).' ]';
11745  }
11746  }
11747  return '/W ['.$w.' ]';
11748  }
11749 
11755  protected function _putfonts() {
11756  $nf = $this->n;
11757  foreach ($this->diffs as $diff) {
11758  //Encodings
11759  $this->_newobj();
11760  $this->_out('<< /Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.'] >>'."\n".'endobj');
11761  }
11762  $mqr = $this->get_mqr();
11763  $this->set_mqr(false);
11764  foreach ($this->FontFiles as $file => $info) {
11765  // search and get font file to embedd
11766  $fontdir = $info['fontdir'];
11767  $file = strtolower($file);
11768  $fontfile = '';
11769  // search files on various directories
11770  if (($fontdir !== false) AND file_exists($fontdir.$file)) {
11771  $fontfile = $fontdir.$file;
11772  } elseif (file_exists($this->_getfontpath().$file)) {
11773  $fontfile = $this->_getfontpath().$file;
11774  } elseif (file_exists($file)) {
11775  $fontfile = $file;
11776  }
11777  if (!$this->empty_string($fontfile)) {
11778  $font = file_get_contents($fontfile);
11779  $compressed = (substr($file, -2) == '.z');
11780  if ((!$compressed) AND (isset($info['length2']))) {
11781  $header = (ord($font{0}) == 128);
11782  if ($header) {
11783  // strip first binary header
11784  $font = substr($font, 6);
11785  }
11786  if ($header AND (ord($font[$info['length1']]) == 128)) {
11787  // strip second binary header
11788  $font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] + 6));
11789  }
11790  } elseif ($info['subset'] AND ((!$compressed) OR ($compressed AND function_exists('gzcompress')))) {
11791  if ($compressed) {
11792  // uncompress font
11793  $font = gzuncompress($font);
11794  }
11795  // merge subset characters
11796  $subsetchars = array(); // used chars
11797  foreach ($info['fontkeys'] as $fontkey) {
11798  $fontinfo = $this->getFontBuffer($fontkey);
11799  $subsetchars += $fontinfo['subsetchars'];
11800  }
11801  // rebuild a font subset
11802  $font = $this->_getTrueTypeFontSubset($font, $subsetchars);
11803  // calculate new font length
11804  $info['length1'] = strlen($font);
11805  if ($compressed) {
11806  // recompress font
11807  $font = gzcompress($font);
11808  }
11809  }
11810  $this->_newobj();
11811  $this->FontFiles[$file]['n'] = $this->n;
11812  $stream = $this->_getrawstream($font);
11813  $out = '<< /Length '.strlen($stream);
11814  if ($compressed) {
11815  $out .= ' /Filter /FlateDecode';
11816  }
11817  $out .= ' /Length1 '.$info['length1'];
11818  if (isset($info['length2'])) {
11819  $out .= ' /Length2 '.$info['length2'].' /Length3 0';
11820  }
11821  $out .= ' >>';
11822  $out .= ' stream'."\n".$stream."\n".'endstream';
11823  $out .= "\n".'endobj';
11824  $this->_out($out);
11825  }
11826  }
11827  $this->set_mqr($mqr);
11828  foreach ($this->fontkeys as $k) {
11829  //Font objects
11830  $font = $this->getFontBuffer($k);
11831  $type = $font['type'];
11832  $name = $font['name'];
11833  if ($type == 'core') {
11834  // standard core font
11835  $out = $this->_getobj($this->font_obj_ids[$k])."\n";
11836  $out .= '<</Type /Font';
11837  $out .= ' /Subtype /Type1';
11838  $out .= ' /BaseFont /'.$name;
11839  $out .= ' /Name /F'.$font['i'];
11840  if ((strtolower($name) != 'symbol') AND (strtolower($name) != 'zapfdingbats')) {
11841  $out .= ' /Encoding /WinAnsiEncoding';
11842  }
11843  if ($k == 'helvetica') {
11844  // add default font for annotations
11845  $this->annotation_fonts[$k] = $font['i'];
11846  }
11847  $out .= ' >>';
11848  $out .= "\n".'endobj';
11849  $this->_out($out);
11850  } elseif (($type == 'Type1') OR ($type == 'TrueType')) {
11851  // additional Type1 or TrueType font
11852  $out = $this->_getobj($this->font_obj_ids[$k])."\n";
11853  $out .= '<</Type /Font';
11854  $out .= ' /Subtype /'.$type;
11855  $out .= ' /BaseFont /'.$name;
11856  $out .= ' /Name /F'.$font['i'];
11857  $out .= ' /FirstChar 32 /LastChar 255';
11858  $out .= ' /Widths '.($this->n + 1).' 0 R';
11859  $out .= ' /FontDescriptor '.($this->n + 2).' 0 R';
11860  if ($font['enc']) {
11861  if (isset($font['diff'])) {
11862  $out .= ' /Encoding '.($nf + $font['diff']).' 0 R';
11863  } else {
11864  $out .= ' /Encoding /WinAnsiEncoding';
11865  }
11866  }
11867  $out .= ' >>';
11868  $out .= "\n".'endobj';
11869  $this->_out($out);
11870  // Widths
11871  $this->_newobj();
11872  $s = '[';
11873  for ($i = 32; $i < 256; ++$i) {
11874  if (isset($font['cw'][$i])) {
11875  $s .= $font['cw'][$i].' ';
11876  } else {
11877  $s .= $font['dw'].' ';
11878  }
11879  }
11880  $s .= ']';
11881  $s .= "\n".'endobj';
11882  $this->_out($s);
11883  //Descriptor
11884  $this->_newobj();
11885  $s = '<</Type /FontDescriptor /FontName /'.$name;
11886  foreach ($font['desc'] as $fdk => $fdv) {
11887  if (is_float($fdv)) {
11888  $fdv = sprintf('%F', $fdv);
11889  }
11890  $s .= ' /'.$fdk.' '.$fdv.'';
11891  }
11892  if (!$this->empty_string($font['file'])) {
11893  $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R';
11894  }
11895  $s .= '>>';
11896  $s .= "\n".'endobj';
11897  $this->_out($s);
11898  } else {
11899  // additional types
11900  $mtd = '_put'.strtolower($type);
11901  if (!method_exists($this, $mtd)) {
11902  $this->Error('Unsupported font type: '.$type);
11903  }
11904  $this->$mtd($font);
11905  }
11906  }
11907  }
11908 
11917  protected function _puttruetypeunicode($font) {
11918  $fontname = '';
11919  if ($font['subset']) {
11920  // change name for font subsetting
11921  $subtag = sprintf('%06u', $font['i']);
11922  $subtag = strtr($subtag, '0123456789', 'ABCDEFGHIJ');
11923  $fontname .= $subtag.'+';
11924  }
11925  $fontname .= $font['name'];
11926  // Type0 Font
11927  // A composite font composed of other fonts, organized hierarchically
11928  $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n";
11929  $out .= '<< /Type /Font';
11930  $out .= ' /Subtype /Type0';
11931  $out .= ' /BaseFont /'.$fontname;
11932  $out .= ' /Name /F'.$font['i'];
11933  $out .= ' /Encoding /'.$font['enc'];
11934  $out .= ' /ToUnicode '.($this->n + 1).' 0 R';
11935  $out .= ' /DescendantFonts ['.($this->n + 2).' 0 R]';
11936  $out .= ' >>';
11937  $out .= "\n".'endobj';
11938  $this->_out($out);
11939  // ToUnicode map for Identity-H
11940  $stream = "/CIDInit /ProcSet findresource begin\n";
11941  $stream .= "12 dict begin\n";
11942  $stream .= "begincmap\n";
11943  $stream .= "/CIDSystemInfo << /Registry (Adobe) /Ordering (UCS) /Supplement 0 >> def\n";
11944  $stream .= "/CMapName /Adobe-Identity-UCS def\n";
11945  $stream .= "/CMapType 2 def\n";
11946  $stream .= "/WMode 0 def\n";
11947  $stream .= "1 begincodespacerange\n";
11948  $stream .= "<0000> <FFFF>\n";
11949  $stream .= "endcodespacerange\n";
11950  $stream .= "100 beginbfrange\n";
11951  $stream .= "<0000> <00ff> <0000>\n";
11952  $stream .= "<0100> <01ff> <0100>\n";
11953  $stream .= "<0200> <02ff> <0200>\n";
11954  $stream .= "<0300> <03ff> <0300>\n";
11955  $stream .= "<0400> <04ff> <0400>\n";
11956  $stream .= "<0500> <05ff> <0500>\n";
11957  $stream .= "<0600> <06ff> <0600>\n";
11958  $stream .= "<0700> <07ff> <0700>\n";
11959  $stream .= "<0800> <08ff> <0800>\n";
11960  $stream .= "<0900> <09ff> <0900>\n";
11961  $stream .= "<0a00> <0aff> <0a00>\n";
11962  $stream .= "<0b00> <0bff> <0b00>\n";
11963  $stream .= "<0c00> <0cff> <0c00>\n";
11964  $stream .= "<0d00> <0dff> <0d00>\n";
11965  $stream .= "<0e00> <0eff> <0e00>\n";
11966  $stream .= "<0f00> <0fff> <0f00>\n";
11967  $stream .= "<1000> <10ff> <1000>\n";
11968  $stream .= "<1100> <11ff> <1100>\n";
11969  $stream .= "<1200> <12ff> <1200>\n";
11970  $stream .= "<1300> <13ff> <1300>\n";
11971  $stream .= "<1400> <14ff> <1400>\n";
11972  $stream .= "<1500> <15ff> <1500>\n";
11973  $stream .= "<1600> <16ff> <1600>\n";
11974  $stream .= "<1700> <17ff> <1700>\n";
11975  $stream .= "<1800> <18ff> <1800>\n";
11976  $stream .= "<1900> <19ff> <1900>\n";
11977  $stream .= "<1a00> <1aff> <1a00>\n";
11978  $stream .= "<1b00> <1bff> <1b00>\n";
11979  $stream .= "<1c00> <1cff> <1c00>\n";
11980  $stream .= "<1d00> <1dff> <1d00>\n";
11981  $stream .= "<1e00> <1eff> <1e00>\n";
11982  $stream .= "<1f00> <1fff> <1f00>\n";
11983  $stream .= "<2000> <20ff> <2000>\n";
11984  $stream .= "<2100> <21ff> <2100>\n";
11985  $stream .= "<2200> <22ff> <2200>\n";
11986  $stream .= "<2300> <23ff> <2300>\n";
11987  $stream .= "<2400> <24ff> <2400>\n";
11988  $stream .= "<2500> <25ff> <2500>\n";
11989  $stream .= "<2600> <26ff> <2600>\n";
11990  $stream .= "<2700> <27ff> <2700>\n";
11991  $stream .= "<2800> <28ff> <2800>\n";
11992  $stream .= "<2900> <29ff> <2900>\n";
11993  $stream .= "<2a00> <2aff> <2a00>\n";
11994  $stream .= "<2b00> <2bff> <2b00>\n";
11995  $stream .= "<2c00> <2cff> <2c00>\n";
11996  $stream .= "<2d00> <2dff> <2d00>\n";
11997  $stream .= "<2e00> <2eff> <2e00>\n";
11998  $stream .= "<2f00> <2fff> <2f00>\n";
11999  $stream .= "<3000> <30ff> <3000>\n";
12000  $stream .= "<3100> <31ff> <3100>\n";
12001  $stream .= "<3200> <32ff> <3200>\n";
12002  $stream .= "<3300> <33ff> <3300>\n";
12003  $stream .= "<3400> <34ff> <3400>\n";
12004  $stream .= "<3500> <35ff> <3500>\n";
12005  $stream .= "<3600> <36ff> <3600>\n";
12006  $stream .= "<3700> <37ff> <3700>\n";
12007  $stream .= "<3800> <38ff> <3800>\n";
12008  $stream .= "<3900> <39ff> <3900>\n";
12009  $stream .= "<3a00> <3aff> <3a00>\n";
12010  $stream .= "<3b00> <3bff> <3b00>\n";
12011  $stream .= "<3c00> <3cff> <3c00>\n";
12012  $stream .= "<3d00> <3dff> <3d00>\n";
12013  $stream .= "<3e00> <3eff> <3e00>\n";
12014  $stream .= "<3f00> <3fff> <3f00>\n";
12015  $stream .= "<4000> <40ff> <4000>\n";
12016  $stream .= "<4100> <41ff> <4100>\n";
12017  $stream .= "<4200> <42ff> <4200>\n";
12018  $stream .= "<4300> <43ff> <4300>\n";
12019  $stream .= "<4400> <44ff> <4400>\n";
12020  $stream .= "<4500> <45ff> <4500>\n";
12021  $stream .= "<4600> <46ff> <4600>\n";
12022  $stream .= "<4700> <47ff> <4700>\n";
12023  $stream .= "<4800> <48ff> <4800>\n";
12024  $stream .= "<4900> <49ff> <4900>\n";
12025  $stream .= "<4a00> <4aff> <4a00>\n";
12026  $stream .= "<4b00> <4bff> <4b00>\n";
12027  $stream .= "<4c00> <4cff> <4c00>\n";
12028  $stream .= "<4d00> <4dff> <4d00>\n";
12029  $stream .= "<4e00> <4eff> <4e00>\n";
12030  $stream .= "<4f00> <4fff> <4f00>\n";
12031  $stream .= "<5000> <50ff> <5000>\n";
12032  $stream .= "<5100> <51ff> <5100>\n";
12033  $stream .= "<5200> <52ff> <5200>\n";
12034  $stream .= "<5300> <53ff> <5300>\n";
12035  $stream .= "<5400> <54ff> <5400>\n";
12036  $stream .= "<5500> <55ff> <5500>\n";
12037  $stream .= "<5600> <56ff> <5600>\n";
12038  $stream .= "<5700> <57ff> <5700>\n";
12039  $stream .= "<5800> <58ff> <5800>\n";
12040  $stream .= "<5900> <59ff> <5900>\n";
12041  $stream .= "<5a00> <5aff> <5a00>\n";
12042  $stream .= "<5b00> <5bff> <5b00>\n";
12043  $stream .= "<5c00> <5cff> <5c00>\n";
12044  $stream .= "<5d00> <5dff> <5d00>\n";
12045  $stream .= "<5e00> <5eff> <5e00>\n";
12046  $stream .= "<5f00> <5fff> <5f00>\n";
12047  $stream .= "<6000> <60ff> <6000>\n";
12048  $stream .= "<6100> <61ff> <6100>\n";
12049  $stream .= "<6200> <62ff> <6200>\n";
12050  $stream .= "<6300> <63ff> <6300>\n";
12051  $stream .= "endbfrange\n";
12052  $stream .= "100 beginbfrange\n";
12053  $stream .= "<6400> <64ff> <6400>\n";
12054  $stream .= "<6500> <65ff> <6500>\n";
12055  $stream .= "<6600> <66ff> <6600>\n";
12056  $stream .= "<6700> <67ff> <6700>\n";
12057  $stream .= "<6800> <68ff> <6800>\n";
12058  $stream .= "<6900> <69ff> <6900>\n";
12059  $stream .= "<6a00> <6aff> <6a00>\n";
12060  $stream .= "<6b00> <6bff> <6b00>\n";
12061  $stream .= "<6c00> <6cff> <6c00>\n";
12062  $stream .= "<6d00> <6dff> <6d00>\n";
12063  $stream .= "<6e00> <6eff> <6e00>\n";
12064  $stream .= "<6f00> <6fff> <6f00>\n";
12065  $stream .= "<7000> <70ff> <7000>\n";
12066  $stream .= "<7100> <71ff> <7100>\n";
12067  $stream .= "<7200> <72ff> <7200>\n";
12068  $stream .= "<7300> <73ff> <7300>\n";
12069  $stream .= "<7400> <74ff> <7400>\n";
12070  $stream .= "<7500> <75ff> <7500>\n";
12071  $stream .= "<7600> <76ff> <7600>\n";
12072  $stream .= "<7700> <77ff> <7700>\n";
12073  $stream .= "<7800> <78ff> <7800>\n";
12074  $stream .= "<7900> <79ff> <7900>\n";
12075  $stream .= "<7a00> <7aff> <7a00>\n";
12076  $stream .= "<7b00> <7bff> <7b00>\n";
12077  $stream .= "<7c00> <7cff> <7c00>\n";
12078  $stream .= "<7d00> <7dff> <7d00>\n";
12079  $stream .= "<7e00> <7eff> <7e00>\n";
12080  $stream .= "<7f00> <7fff> <7f00>\n";
12081  $stream .= "<8000> <80ff> <8000>\n";
12082  $stream .= "<8100> <81ff> <8100>\n";
12083  $stream .= "<8200> <82ff> <8200>\n";
12084  $stream .= "<8300> <83ff> <8300>\n";
12085  $stream .= "<8400> <84ff> <8400>\n";
12086  $stream .= "<8500> <85ff> <8500>\n";
12087  $stream .= "<8600> <86ff> <8600>\n";
12088  $stream .= "<8700> <87ff> <8700>\n";
12089  $stream .= "<8800> <88ff> <8800>\n";
12090  $stream .= "<8900> <89ff> <8900>\n";
12091  $stream .= "<8a00> <8aff> <8a00>\n";
12092  $stream .= "<8b00> <8bff> <8b00>\n";
12093  $stream .= "<8c00> <8cff> <8c00>\n";
12094  $stream .= "<8d00> <8dff> <8d00>\n";
12095  $stream .= "<8e00> <8eff> <8e00>\n";
12096  $stream .= "<8f00> <8fff> <8f00>\n";
12097  $stream .= "<9000> <90ff> <9000>\n";
12098  $stream .= "<9100> <91ff> <9100>\n";
12099  $stream .= "<9200> <92ff> <9200>\n";
12100  $stream .= "<9300> <93ff> <9300>\n";
12101  $stream .= "<9400> <94ff> <9400>\n";
12102  $stream .= "<9500> <95ff> <9500>\n";
12103  $stream .= "<9600> <96ff> <9600>\n";
12104  $stream .= "<9700> <97ff> <9700>\n";
12105  $stream .= "<9800> <98ff> <9800>\n";
12106  $stream .= "<9900> <99ff> <9900>\n";
12107  $stream .= "<9a00> <9aff> <9a00>\n";
12108  $stream .= "<9b00> <9bff> <9b00>\n";
12109  $stream .= "<9c00> <9cff> <9c00>\n";
12110  $stream .= "<9d00> <9dff> <9d00>\n";
12111  $stream .= "<9e00> <9eff> <9e00>\n";
12112  $stream .= "<9f00> <9fff> <9f00>\n";
12113  $stream .= "<a000> <a0ff> <a000>\n";
12114  $stream .= "<a100> <a1ff> <a100>\n";
12115  $stream .= "<a200> <a2ff> <a200>\n";
12116  $stream .= "<a300> <a3ff> <a300>\n";
12117  $stream .= "<a400> <a4ff> <a400>\n";
12118  $stream .= "<a500> <a5ff> <a500>\n";
12119  $stream .= "<a600> <a6ff> <a600>\n";
12120  $stream .= "<a700> <a7ff> <a700>\n";
12121  $stream .= "<a800> <a8ff> <a800>\n";
12122  $stream .= "<a900> <a9ff> <a900>\n";
12123  $stream .= "<aa00> <aaff> <aa00>\n";
12124  $stream .= "<ab00> <abff> <ab00>\n";
12125  $stream .= "<ac00> <acff> <ac00>\n";
12126  $stream .= "<ad00> <adff> <ad00>\n";
12127  $stream .= "<ae00> <aeff> <ae00>\n";
12128  $stream .= "<af00> <afff> <af00>\n";
12129  $stream .= "<b000> <b0ff> <b000>\n";
12130  $stream .= "<b100> <b1ff> <b100>\n";
12131  $stream .= "<b200> <b2ff> <b200>\n";
12132  $stream .= "<b300> <b3ff> <b300>\n";
12133  $stream .= "<b400> <b4ff> <b400>\n";
12134  $stream .= "<b500> <b5ff> <b500>\n";
12135  $stream .= "<b600> <b6ff> <b600>\n";
12136  $stream .= "<b700> <b7ff> <b700>\n";
12137  $stream .= "<b800> <b8ff> <b800>\n";
12138  $stream .= "<b900> <b9ff> <b900>\n";
12139  $stream .= "<ba00> <baff> <ba00>\n";
12140  $stream .= "<bb00> <bbff> <bb00>\n";
12141  $stream .= "<bc00> <bcff> <bc00>\n";
12142  $stream .= "<bd00> <bdff> <bd00>\n";
12143  $stream .= "<be00> <beff> <be00>\n";
12144  $stream .= "<bf00> <bfff> <bf00>\n";
12145  $stream .= "<c000> <c0ff> <c000>\n";
12146  $stream .= "<c100> <c1ff> <c100>\n";
12147  $stream .= "<c200> <c2ff> <c200>\n";
12148  $stream .= "<c300> <c3ff> <c300>\n";
12149  $stream .= "<c400> <c4ff> <c400>\n";
12150  $stream .= "<c500> <c5ff> <c500>\n";
12151  $stream .= "<c600> <c6ff> <c600>\n";
12152  $stream .= "<c700> <c7ff> <c700>\n";
12153  $stream .= "endbfrange\n";
12154  $stream .= "56 beginbfrange\n";
12155  $stream .= "<c800> <c8ff> <c800>\n";
12156  $stream .= "<c900> <c9ff> <c900>\n";
12157  $stream .= "<ca00> <caff> <ca00>\n";
12158  $stream .= "<cb00> <cbff> <cb00>\n";
12159  $stream .= "<cc00> <ccff> <cc00>\n";
12160  $stream .= "<cd00> <cdff> <cd00>\n";
12161  $stream .= "<ce00> <ceff> <ce00>\n";
12162  $stream .= "<cf00> <cfff> <cf00>\n";
12163  $stream .= "<d000> <d0ff> <d000>\n";
12164  $stream .= "<d100> <d1ff> <d100>\n";
12165  $stream .= "<d200> <d2ff> <d200>\n";
12166  $stream .= "<d300> <d3ff> <d300>\n";
12167  $stream .= "<d400> <d4ff> <d400>\n";
12168  $stream .= "<d500> <d5ff> <d500>\n";
12169  $stream .= "<d600> <d6ff> <d600>\n";
12170  $stream .= "<d700> <d7ff> <d700>\n";
12171  $stream .= "<d800> <d8ff> <d800>\n";
12172  $stream .= "<d900> <d9ff> <d900>\n";
12173  $stream .= "<da00> <daff> <da00>\n";
12174  $stream .= "<db00> <dbff> <db00>\n";
12175  $stream .= "<dc00> <dcff> <dc00>\n";
12176  $stream .= "<dd00> <ddff> <dd00>\n";
12177  $stream .= "<de00> <deff> <de00>\n";
12178  $stream .= "<df00> <dfff> <df00>\n";
12179  $stream .= "<e000> <e0ff> <e000>\n";
12180  $stream .= "<e100> <e1ff> <e100>\n";
12181  $stream .= "<e200> <e2ff> <e200>\n";
12182  $stream .= "<e300> <e3ff> <e300>\n";
12183  $stream .= "<e400> <e4ff> <e400>\n";
12184  $stream .= "<e500> <e5ff> <e500>\n";
12185  $stream .= "<e600> <e6ff> <e600>\n";
12186  $stream .= "<e700> <e7ff> <e700>\n";
12187  $stream .= "<e800> <e8ff> <e800>\n";
12188  $stream .= "<e900> <e9ff> <e900>\n";
12189  $stream .= "<ea00> <eaff> <ea00>\n";
12190  $stream .= "<eb00> <ebff> <eb00>\n";
12191  $stream .= "<ec00> <ecff> <ec00>\n";
12192  $stream .= "<ed00> <edff> <ed00>\n";
12193  $stream .= "<ee00> <eeff> <ee00>\n";
12194  $stream .= "<ef00> <efff> <ef00>\n";
12195  $stream .= "<f000> <f0ff> <f000>\n";
12196  $stream .= "<f100> <f1ff> <f100>\n";
12197  $stream .= "<f200> <f2ff> <f200>\n";
12198  $stream .= "<f300> <f3ff> <f300>\n";
12199  $stream .= "<f400> <f4ff> <f400>\n";
12200  $stream .= "<f500> <f5ff> <f500>\n";
12201  $stream .= "<f600> <f6ff> <f600>\n";
12202  $stream .= "<f700> <f7ff> <f700>\n";
12203  $stream .= "<f800> <f8ff> <f800>\n";
12204  $stream .= "<f900> <f9ff> <f900>\n";
12205  $stream .= "<fa00> <faff> <fa00>\n";
12206  $stream .= "<fb00> <fbff> <fb00>\n";
12207  $stream .= "<fc00> <fcff> <fc00>\n";
12208  $stream .= "<fd00> <fdff> <fd00>\n";
12209  $stream .= "<fe00> <feff> <fe00>\n";
12210  $stream .= "<ff00> <ffff> <ff00>\n";
12211  $stream .= "endbfrange\n";
12212  $stream .= "endcmap\n";
12213  $stream .= "CMapName currentdict /CMap defineresource pop\n";
12214  $stream .= "end\n";
12215  $stream .= "end";
12216  // ToUnicode Object
12217  $this->_newobj();
12218  $stream = ($this->compress) ? gzcompress($stream) : $stream;
12219  $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
12220  $stream = $this->_getrawstream($stream);
12221  $this->_out('<<'.$filter.'/Length '.strlen($stream).'>> stream'."\n".$stream."\n".'endstream'."\n".'endobj');
12222  // CIDFontType2
12223  // A CIDFont whose glyph descriptions are based on TrueType font technology
12224  $oid = $this->_newobj();
12225  $out = '<< /Type /Font';
12226  $out .= ' /Subtype /CIDFontType2';
12227  $out .= ' /BaseFont /'.$fontname;
12228  // A dictionary containing entries that define the character collection of the CIDFont.
12229  $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
12230  $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
12231  $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
12232  $out .= ' /CIDSystemInfo << '.$cidinfo.' >>';
12233  $out .= ' /FontDescriptor '.($this->n + 1).' 0 R';
12234  $out .= ' /DW '.$font['dw']; // default width
12235  $out .= "\n".$this->_putfontwidths($font, 0);
12236  if (isset($font['ctg']) AND (!$this->empty_string($font['ctg']))) {
12237  $out .= "\n".'/CIDToGIDMap '.($this->n + 2).' 0 R';
12238  }
12239  $out .= ' >>';
12240  $out .= "\n".'endobj';
12241  $this->_out($out);
12242  // Font descriptor
12243  // A font descriptor describing the CIDFont default metrics other than its glyph widths
12244  $this->_newobj();
12245  $out = '<< /Type /FontDescriptor';
12246  $out .= ' /FontName /'.$fontname;
12247  foreach ($font['desc'] as $key => $value) {
12248  if (is_float($value)) {
12249  $value = sprintf('%F', $value);
12250  }
12251  $out .= ' /'.$key.' '.$value;
12252  }
12253  $fontdir = false;
12254  if (!$this->empty_string($font['file'])) {
12255  // A stream containing a TrueType font
12256  $out .= ' /FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R';
12257  $fontdir = $this->FontFiles[$font['file']]['fontdir'];
12258  }
12259  $out .= ' >>';
12260  $out .= "\n".'endobj';
12261  $this->_out($out);
12262  if (isset($font['ctg']) AND (!$this->empty_string($font['ctg']))) {
12263  $this->_newobj();
12264  // Embed CIDToGIDMap
12265  // A specification of the mapping from CIDs to glyph indices
12266  // search and get CTG font file to embedd
12267  $ctgfile = strtolower($font['ctg']);
12268  // search and get ctg font file to embedd
12269  $fontfile = '';
12270  // search files on various directories
12271  if (($fontdir !== false) AND file_exists($fontdir.$ctgfile)) {
12272  $fontfile = $fontdir.$ctgfile;
12273  } elseif (file_exists($this->_getfontpath().$ctgfile)) {
12274  $fontfile = $this->_getfontpath().$ctgfile;
12275  } elseif (file_exists($ctgfile)) {
12276  $fontfile = $ctgfile;
12277  }
12278  if ($this->empty_string($fontfile)) {
12279  $this->Error('Font file not found: '.$ctgfile);
12280  }
12281  $stream = $this->_getrawstream(file_get_contents($fontfile));
12282  $out = '<< /Length '.strlen($stream).'';
12283  if (substr($fontfile, -2) == '.z') { // check file extension
12284  // Decompresses data encoded using the public-domain
12285  // zlib/deflate compression method, reproducing the
12286  // original text or binary data
12287  $out .= ' /Filter /FlateDecode';
12288  }
12289  $out .= ' >>';
12290  $out .= ' stream'."\n".$stream."\n".'endstream';
12291  $out .= "\n".'endobj';
12292  $this->_out($out);
12293  }
12294  }
12295 
12304  protected function _putcidfont0($font) {
12305  $cidoffset = 0;
12306  if (!isset($font['cw'][1])) {
12307  $cidoffset = 31;
12308  }
12309  if (isset($font['cidinfo']['uni2cid'])) {
12310  // convert unicode to cid.
12311  $uni2cid = $font['cidinfo']['uni2cid'];
12312  $cw = array();
12313  foreach ($font['cw'] as $uni => $width) {
12314  if (isset($uni2cid[$uni])) {
12315  $cw[($uni2cid[$uni] + $cidoffset)] = $width;
12316  } elseif ($uni < 256) {
12317  $cw[$uni] = $width;
12318  } // else unknown character
12319  }
12320  $font = array_merge($font, array('cw' => $cw));
12321  }
12322  $name = $font['name'];
12323  $enc = $font['enc'];
12324  if ($enc) {
12325  $longname = $name.'-'.$enc;
12326  } else {
12327  $longname = $name;
12328  }
12329  $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n";
12330  $out .= '<</Type /Font';
12331  $out .= ' /Subtype /Type0';
12332  $out .= ' /BaseFont /'.$longname;
12333  $out .= ' /Name /F'.$font['i'];
12334  if ($enc) {
12335  $out .= ' /Encoding /'.$enc;
12336  }
12337  $out .= ' /DescendantFonts ['.($this->n + 1).' 0 R]';
12338  $out .= ' >>';
12339  $out .= "\n".'endobj';
12340  $this->_out($out);
12341  $oid = $this->_newobj();
12342  $out = '<</Type /Font';
12343  $out .= ' /Subtype /CIDFontType0';
12344  $out .= ' /BaseFont /'.$name;
12345  $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
12346  $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
12347  $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
12348  $out .= ' /CIDSystemInfo <<'.$cidinfo.'>>';
12349  $out .= ' /FontDescriptor '.($this->n + 1).' 0 R';
12350  $out .= ' /DW '.$font['dw'];
12351  $out .= "\n".$this->_putfontwidths($font, $cidoffset);
12352  $out .= ' >>';
12353  $out .= "\n".'endobj';
12354  $this->_out($out);
12355  $this->_newobj();
12356  $s = '<</Type /FontDescriptor /FontName /'.$name;
12357  foreach ($font['desc'] as $k => $v) {
12358  if ($k != 'Style') {
12359  if (is_float($v)) {
12360  $v = sprintf('%F', $v);
12361  }
12362  $s .= ' /'.$k.' '.$v.'';
12363  }
12364  }
12365  $s .= '>>';
12366  $s .= "\n".'endobj';
12367  $this->_out($s);
12368  }
12369 
12374  protected function _putimages() {
12375  $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
12376  foreach ($this->imagekeys as $file) {
12377  $info = $this->getImageBuffer($file);
12378  // set object for alternate images array
12379  if ((!$this->pdfa_mode) AND isset($info['altimgs']) AND !empty($info['altimgs'])) {
12380  $altoid = $this->_newobj();
12381  $out = '[';
12382  foreach ($info['altimgs'] as $altimage) {
12383  if (isset($this->xobjects['I'.$altimage[0]]['n'])) {
12384  $out .= ' << /Image '.$this->xobjects['I'.$altimage[0]]['n'].' 0 R';
12385  $out .= ' /DefaultForPrinting';
12386  if ($altimage[1] === true) {
12387  $out .= ' true';
12388  } else {
12389  $out .= ' false';
12390  }
12391  $out .= ' >>';
12392  }
12393  }
12394  $out .= ' ]';
12395  $out .= "\n".'endobj';
12396  $this->_out($out);
12397  }
12398  // set image object
12399  $oid = $this->_newobj();
12400  $this->xobjects['I'.$info['i']] = array('n' => $oid);
12401  $this->setImageSubBuffer($file, 'n', $this->n);
12402  $out = '<</Type /XObject';
12403  $out .= ' /Subtype /Image';
12404  $out .= ' /Width '.$info['w'];
12405  $out .= ' /Height '.$info['h'];
12406  if (array_key_exists('masked', $info)) {
12407  $out .= ' /SMask '.($this->n - 1).' 0 R';
12408  }
12409  // set color space
12410  $icc = false;
12411  if (isset($info['icc']) AND ($info['icc'] !== false)) {
12412  // ICC Colour Space
12413  $icc = true;
12414  $out .= ' /ColorSpace [/ICCBased '.($this->n + 1).' 0 R]';
12415  } elseif ($info['cs'] == 'Indexed') {
12416  // Indexed Colour Space
12417  $out .= ' /ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n + 1).' 0 R]';
12418  } else {
12419  // Device Colour Space
12420  $out .= ' /ColorSpace /'.$info['cs'];
12421  }
12422  if ($info['cs'] == 'DeviceCMYK') {
12423  $out .= ' /Decode [1 0 1 0 1 0 1 0]';
12424  }
12425  $out .= ' /BitsPerComponent '.$info['bpc'];
12426  if (isset($altoid) AND ($altoid > 0)) {
12427  // reference to alternate images dictionary
12428  $out .= ' /Alternates '.$altoid.' 0 R';
12429  }
12430  if (isset($info['exurl']) AND !empty($info['exurl'])) {
12431  // external stream
12432  $out .= ' /Length 0';
12433  $out .= ' /F << /FS /URL /F '.$this->_datastring($info['exurl'], $oid).' >>';
12434  if (isset($info['f'])) {
12435  $out .= ' /FFilter /'.$info['f'];
12436  }
12437  $out .= ' >>';
12438  $out .= ' stream'."\n".'endstream';
12439  } else {
12440  if (isset($info['f'])) {
12441  $out .= ' /Filter /'.$info['f'];
12442  }
12443  if (isset($info['parms'])) {
12444  $out .= ' '.$info['parms'];
12445  }
12446  if (isset($info['trns']) AND is_array($info['trns'])) {
12447  $trns = '';
12448  $count_info = count($info['trns']);
12449  for ($i=0; $i < $count_info; ++$i) {
12450  $trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
12451  }
12452  $out .= ' /Mask ['.$trns.']';
12453  }
12454  $stream = $this->_getrawstream($info['data']);
12455  $out .= ' /Length '.strlen($stream).' >>';
12456  $out .= ' stream'."\n".$stream."\n".'endstream';
12457  }
12458  $out .= "\n".'endobj';
12459  $this->_out($out);
12460  if ($icc) {
12461  // ICC colour profile
12462  $this->_newobj();
12463  $icc = ($this->compress) ? gzcompress($info['icc']) : $info['icc'];
12464  $icc = $this->_getrawstream($icc);
12465  $this->_out('<</N '.$info['ch'].' /Alternate /'.$info['cs'].' '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj');
12466  } elseif ($info['cs'] == 'Indexed') {
12467  // colour palette
12468  $this->_newobj();
12469  $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
12470  $pal = $this->_getrawstream($pal);
12471  $this->_out('<<'.$filter.'/Length '.strlen($pal).'>> stream'."\n".$pal."\n".'endstream'."\n".'endobj');
12472  }
12473  }
12474  }
12475 
12483  protected function _putxobjects() {
12484  foreach ($this->xobjects as $key => $data) {
12485  if (isset($data['outdata'])) {
12486  $stream = trim($data['outdata']);
12487  $out = $this->_getobj($data['n'])."\n";
12488  $out .= '<<';
12489  $out .= ' /Type /XObject';
12490  $out .= ' /Subtype /Form';
12491  $out .= ' /FormType 1';
12492  if ($this->compress) {
12493  $stream = gzcompress($stream);
12494  $out .= ' /Filter /FlateDecode';
12495  }
12496  $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));
12497  $out .= ' /Matrix [1 0 0 1 0 0]';
12498  $out .= ' /Resources <<';
12499  $out .= ' /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
12500  if (!$this->pdfa_mode) {
12501  // transparency
12502  if (isset($data['extgstates']) AND !empty($data['extgstates'])) {
12503  $out .= ' /ExtGState <<';
12504  foreach ($data['extgstates'] as $k => $extgstate) {
12505  if (isset($this->extgstates[$k]['name'])) {
12506  $out .= ' /'.$this->extgstates[$k]['name'];
12507  } else {
12508  $out .= ' /GS'.$k;
12509  }
12510  $out .= ' '.$this->extgstates[$k]['n'].' 0 R';
12511  }
12512  $out .= ' >>';
12513  }
12514  if (isset($data['gradients']) AND !empty($data['gradients'])) {
12515  $gp = '';
12516  $gs = '';
12517  foreach ($data['gradients'] as $id => $grad) {
12518  // gradient patterns
12519  $gp .= ' /p'.$id.' '.$this->gradients[$id]['pattern'].' 0 R';
12520  // gradient shadings
12521  $gs .= ' /Sh'.$id.' '.$this->gradients[$id]['id'].' 0 R';
12522  }
12523  $out .= ' /Pattern <<'.$gp.' >>';
12524  $out .= ' /Shading <<'.$gs.' >>';
12525  }
12526  }
12527  // spot colors
12528  if (isset($data['spot_colors']) AND !empty($data['spot_colors'])) {
12529  $out .= ' /ColorSpace <<';
12530  foreach ($data['spot_colors'] as $name => $color) {
12531  $out .= ' /CS'.$color['i'].' '.$this->spot_colors[$name]['n'].' 0 R';
12532  }
12533  $out .= ' >>';
12534  }
12535  // fonts
12536  if (!empty($data['fonts'])) {
12537  $out .= ' /Font <<';
12538  foreach ($data['fonts'] as $fontkey => $fontid) {
12539  $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R';
12540  }
12541  $out .= ' >>';
12542  }
12543  // images or nested xobjects
12544  if (!empty($data['images']) OR !empty($data['xobjects'])) {
12545  $out .= ' /XObject <<';
12546  foreach ($data['images'] as $imgid) {
12547  $out .= ' /I'.$imgid.' '.$this->xobjects['I'.$imgid]['n'].' 0 R';
12548  }
12549  foreach ($data['xobjects'] as $sub_id => $sub_objid) {
12550  $out .= ' /'.$sub_id.' '.$sub_objid['n'].' 0 R';
12551  }
12552  $out .= ' >>';
12553  }
12554  $out .= ' >>'; //end resources
12555  if (isset($data['group']) AND ($data['group'] !== false)) {
12556  // set transparency group
12557  $out .= ' /Group << /Type /Group /S /Transparency';
12558  if (is_array($data['group'])) {
12559  if (isset($data['group']['CS']) AND !empty($data['group']['CS'])) {
12560  $out .= ' /CS /'.$data['group']['CS'];
12561  }
12562  if (isset($data['group']['I'])) {
12563  $out .= ' /I /'.($data['group']['I']===true?'true':'false');
12564  }
12565  if (isset($data['group']['K'])) {
12566  $out .= ' /K /'.($data['group']['K']===true?'true':'false');
12567  }
12568  }
12569  $out .= ' >>';
12570  }
12571  $stream = $this->_getrawstream($stream, $data['n']);
12572  $out .= ' /Length '.strlen($stream);
12573  $out .= ' >>';
12574  $out .= ' stream'."\n".$stream."\n".'endstream';
12575  $out .= "\n".'endobj';
12576  $this->_out($out);
12577  }
12578  }
12579  }
12580 
12586  protected function _putspotcolors() {
12587  foreach ($this->spot_colors as $name => $color) {
12588  $this->_newobj();
12589  $this->spot_colors[$name]['n'] = $this->n;
12590  $out = '[/Separation /'.str_replace(' ', '#20', $name);
12591  $out .= ' /DeviceCMYK <<';
12592  $out .= ' /Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0]';
12593  $out .= ' '.sprintf('/C1 [%F %F %F %F] ', ($color['C'] / 100), ($color['M'] / 100), ($color['Y'] / 100), ($color['K'] / 100));
12594  $out .= ' /FunctionType 2 /Domain [0 1] /N 1>>]';
12595  $out .= "\n".'endobj';
12596  $this->_out($out);
12597  }
12598  }
12599 
12606  protected function _getxobjectdict() {
12607  $out = '';
12608  foreach ($this->xobjects as $id => $objid) {
12609  $out .= ' /'.$id.' '.$objid['n'].' 0 R';
12610  }
12611  return $out;
12612  }
12613 
12618  protected function _putresourcedict() {
12619  $out = $this->_getobj(2)."\n";
12620  $out .= '<< /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
12621  $out .= ' /Font <<';
12622  foreach ($this->fontkeys as $fontkey) {
12623  $font = $this->getFontBuffer($fontkey);
12624  $out .= ' /F'.$font['i'].' '.$font['n'].' 0 R';
12625  }
12626  $out .= ' >>';
12627  $out .= ' /XObject <<';
12628  $out .= $this->_getxobjectdict();
12629  $out .= ' >>';
12630  // layers
12631  if (!empty($this->pdflayers)) {
12632  $out .= ' /Properties <<';
12633  foreach ($this->pdflayers as $layer) {
12634  $out .= ' /'.$layer['layer'].' '.$layer['objid'].' 0 R';
12635  }
12636  $out .= ' >>';
12637  }
12638  if (!$this->pdfa_mode) {
12639  // transparency
12640  if (isset($this->extgstates) AND !empty($this->extgstates)) {
12641  $out .= ' /ExtGState <<';
12642  foreach ($this->extgstates as $k => $extgstate) {
12643  if (isset($extgstate['name'])) {
12644  $out .= ' /'.$extgstate['name'];
12645  } else {
12646  $out .= ' /GS'.$k;
12647  }
12648  $out .= ' '.$extgstate['n'].' 0 R';
12649  }
12650  $out .= ' >>';
12651  }
12652  if (isset($this->gradients) AND !empty($this->gradients)) {
12653  $gp = '';
12654  $gs = '';
12655  foreach ($this->gradients as $id => $grad) {
12656  // gradient patterns
12657  $gp .= ' /p'.$id.' '.$grad['pattern'].' 0 R';
12658  // gradient shadings
12659  $gs .= ' /Sh'.$id.' '.$grad['id'].' 0 R';
12660  }
12661  $out .= ' /Pattern <<'.$gp.' >>';
12662  $out .= ' /Shading <<'.$gs.' >>';
12663  }
12664  }
12665  // spot colors
12666  if (isset($this->spot_colors) AND !empty($this->spot_colors)) {
12667  $out .= ' /ColorSpace <<';
12668  foreach ($this->spot_colors as $color) {
12669  $out .= ' /CS'.$color['i'].' '.$color['n'].' 0 R';
12670  }
12671  $out .= ' >>';
12672  }
12673  $out .= ' >>';
12674  $out .= "\n".'endobj';
12675  $this->_out($out);
12676  }
12677 
12682  protected function _putresources() {
12683  $this->_putextgstates();
12684  $this->_putocg();
12685  $this->_putfonts();
12686  $this->_putimages();
12687  $this->_putspotcolors();
12688  $this->_putshaders();
12689  $this->_putxobjects();
12690  $this->_putresourcedict();
12691  $this->_putdests();
12692  $this->_putbookmarks();
12693  $this->_putEmbeddedFiles();
12694  $this->_putannotsobjs();
12695  $this->_putjavascript();
12696  $this->_putencryption();
12697  }
12698 
12705  protected function _putinfo() {
12706  $oid = $this->_newobj();
12707  $out = '<<';
12708  // store current isunicode value
12709  $prev_isunicode = $this->isunicode;
12710  if ($this->docinfounicode) {
12711  $this->isunicode = true;
12712  }
12713  if (!$this->empty_string($this->title)) {
12714  // The document's title.
12715  $out .= ' /Title '.$this->_textstring($this->title, $oid);
12716  }
12717  if (!$this->empty_string($this->author)) {
12718  // The name of the person who created the document.
12719  $out .= ' /Author '.$this->_textstring($this->author, $oid);
12720  }
12721  if (!$this->empty_string($this->subject)) {
12722  // The subject of the document.
12723  $out .= ' /Subject '.$this->_textstring($this->subject, $oid);
12724  }
12725  if (!$this->empty_string($this->keywords)) {
12726  // Keywords associated with the document.
12727  $out .= ' /Keywords '.$this->_textstring($this->keywords.' TCPDF', $oid);
12728  }
12729  if (!$this->empty_string($this->creator)) {
12730  // 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.
12731  $out .= ' /Creator '.$this->_textstring($this->creator, $oid);
12732  }
12733  // restore previous isunicode value
12734  $this->isunicode = $prev_isunicode;
12735  // default producer
12736  $out .= ' /Producer '.$this->_textstring($this->pdfproducer, $oid);
12737  // The date and time the document was created, in human-readable form
12738  $out .= ' /CreationDate '.$this->_datestring(0, $this->doc_creation_timestamp);
12739  // The date and time the document was most recently modified, in human-readable form
12740  $out .= ' /ModDate '.$this->_datestring(0, $this->doc_modification_timestamp);
12741  // A name object indicating whether the document has been modified to include trapping information
12742  $out .= ' /Trapped /False';
12743  $out .= ' >>';
12744  $out .= "\n".'endobj';
12745  $this->_out($out);
12746  return $oid;
12747  }
12748 
12756  public function setExtraXMP($xmp) {
12757  $this->custom_xmp = $xmp;
12758  }
12759 
12766  protected function _putXMP() {
12767  $oid = $this->_newobj();
12768  // store current isunicode value
12769  $prev_isunicode = $this->isunicode;
12770  $this->isunicode = true;
12771  $prev_encrypted = $this->encrypted;
12772  $this->encrypted = false;
12773  // set XMP data
12774  $xmp = '<?xpacket begin="'.$this->unichr(0xfeff).'" id="W5M0MpCehiHzreSzNTczkc9d"?>'."\n";
12775  $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";
12776  $xmp .= "\t".'<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">'."\n";
12777  $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">'."\n";
12778  $xmp .= "\t\t\t".'<dc:format>application/pdf</dc:format>'."\n";
12779  $xmp .= "\t\t\t".'<dc:title>'."\n";
12780  $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n";
12781  $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.$this->_escapeXML($this->title).'</rdf:li>'."\n";
12782  $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n";
12783  $xmp .= "\t\t\t".'</dc:title>'."\n";
12784  $xmp .= "\t\t\t".'<dc:creator>'."\n";
12785  $xmp .= "\t\t\t\t".'<rdf:Seq>'."\n";
12786  $xmp .= "\t\t\t\t\t".'<rdf:li>'.$this->_escapeXML($this->author).'</rdf:li>'."\n";
12787  $xmp .= "\t\t\t\t".'</rdf:Seq>'."\n";
12788  $xmp .= "\t\t\t".'</dc:creator>'."\n";
12789  $xmp .= "\t\t\t".'<dc:description>'."\n";
12790  $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n";
12791  $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.$this->_escapeXML($this->subject).'</rdf:li>'."\n";
12792  $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n";
12793  $xmp .= "\t\t\t".'</dc:description>'."\n";
12794  $xmp .= "\t\t\t".'<dc:subject>'."\n";
12795  $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n";
12796  $xmp .= "\t\t\t\t\t".'<rdf:li>'.$this->_escapeXML($this->keywords).' TCPDF</rdf:li>'."\n";
12797  $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n";
12798  $xmp .= "\t\t\t".'</dc:subject>'."\n";
12799  $xmp .= "\t\t".'</rdf:Description>'."\n";
12800  // convert doc creation date format
12801  $dcdate = $this->getFormattedDate($this->doc_creation_timestamp);
12802  $doccreationdate = substr($dcdate, 0, 4).'-'.substr($dcdate, 4, 2).'-'.substr($dcdate, 6, 2);
12803  $doccreationdate .= 'T'.substr($dcdate, 8, 2).':'.substr($dcdate, 10, 2).':'.substr($dcdate, 12, 2);
12804  $doccreationdate .= '+'.substr($dcdate, 15, 2).':'.substr($dcdate, 18, 2);
12805  $doccreationdate = $this->_escapeXML($doccreationdate);
12806  // convert doc modification date format
12807  $dmdate = $this->getFormattedDate($this->doc_modification_timestamp);
12808  $docmoddate = substr($dmdate, 0, 4).'-'.substr($dmdate, 4, 2).'-'.substr($dmdate, 6, 2);
12809  $docmoddate .= 'T'.substr($dmdate, 8, 2).':'.substr($dmdate, 10, 2).':'.substr($dmdate, 12, 2);
12810  $docmoddate .= '+'.substr($dmdate, 15, 2).':'.substr($dmdate, 18, 2);
12811  $docmoddate = $this->_escapeXML($docmoddate);
12812  $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/">'."\n";
12813  $xmp .= "\t\t\t".'<xmp:CreateDate>'.$doccreationdate.'</xmp:CreateDate>'."\n";
12814  $xmp .= "\t\t\t".'<xmp:CreatorTool>'.$this->creator.'</xmp:CreatorTool>'."\n";
12815  $xmp .= "\t\t\t".'<xmp:ModifyDate>'.$docmoddate.'</xmp:ModifyDate>'."\n";
12816  $xmp .= "\t\t\t".'<xmp:MetadataDate>'.$doccreationdate.'</xmp:MetadataDate>'."\n";
12817  $xmp .= "\t\t".'</rdf:Description>'."\n";
12818  $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdf="http://ns.adobe.com/pdf/1.3/">'."\n";
12819  $xmp .= "\t\t\t".'<pdf:Keywords>'.$this->_escapeXML($this->keywords).' TCPDF</pdf:Keywords>'."\n";
12820  $xmp .= "\t\t\t".'<pdf:Producer>'.$this->_escapeXML($this->pdfproducer).'</pdf:Producer>'."\n";
12821  $xmp .= "\t\t".'</rdf:Description>'."\n";
12822  $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/">'."\n";
12823  $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);
12824  $xmp .= "\t\t\t".'<xmpMM:DocumentID>'.$uuid.'</xmpMM:DocumentID>'."\n";
12825  $xmp .= "\t\t\t".'<xmpMM:InstanceID>'.$uuid.'</xmpMM:InstanceID>'."\n";
12826  $xmp .= "\t\t".'</rdf:Description>'."\n";
12827  if ($this->pdfa_mode) {
12828  $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/">'."\n";
12829  $xmp .= "\t\t\t".'<pdfaid:part>1</pdfaid:part>'."\n";
12830  $xmp .= "\t\t\t".'<pdfaid:conformance>B</pdfaid:conformance>'."\n";
12831  $xmp .= "\t\t".'</rdf:Description>'."\n";
12832  }
12833  // XMP extension schemas
12834  $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";
12835  $xmp .= "\t\t\t".'<pdfaExtension:schemas>'."\n";
12836  $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n";
12837  $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
12838  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/pdf/1.3/</pdfaSchema:namespaceURI>'."\n";
12839  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdf</pdfaSchema:prefix>'."\n";
12840  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>Adobe PDF Schema</pdfaSchema:schema>'."\n";
12841  $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
12842  $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
12843  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/xap/1.0/mm/</pdfaSchema:namespaceURI>'."\n";
12844  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>xmpMM</pdfaSchema:prefix>'."\n";
12845  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>XMP Media Management Schema</pdfaSchema:schema>'."\n";
12846  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
12847  $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
12848  $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
12849  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
12850  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>UUID based identifier for specific incarnation of a document</pdfaProperty:description>'."\n";
12851  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>InstanceID</pdfaProperty:name>'."\n";
12852  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>URI</pdfaProperty:valueType>'."\n";
12853  $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
12854  $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
12855  $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
12856  $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
12857  $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
12858  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://www.aiim.org/pdfa/ns/id/</pdfaSchema:namespaceURI>'."\n";
12859  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdfaid</pdfaSchema:prefix>'."\n";
12860  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>PDF/A ID Schema</pdfaSchema:schema>'."\n";
12861  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
12862  $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
12863  $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
12864  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
12865  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Part of PDF/A standard</pdfaProperty:description>'."\n";
12866  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>part</pdfaProperty:name>'."\n";
12867  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Integer</pdfaProperty:valueType>'."\n";
12868  $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
12869  $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
12870  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
12871  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Amendment of PDF/A standard</pdfaProperty:description>'."\n";
12872  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>amd</pdfaProperty:name>'."\n";
12873  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n";
12874  $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
12875  $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
12876  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
12877  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Conformance level of PDF/A standard</pdfaProperty:description>'."\n";
12878  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>conformance</pdfaProperty:name>'."\n";
12879  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n";
12880  $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
12881  $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
12882  $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
12883  $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
12884  $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n";
12885  $xmp .= "\t\t\t".'</pdfaExtension:schemas>'."\n";
12886  $xmp .= "\t\t".'</rdf:Description>'."\n";
12887  $xmp .= "\t".'</rdf:RDF>'."\n";
12888  $xmp .= $this->custom_xmp;
12889  $xmp .= '</x:xmpmeta>'."\n";
12890  $xmp .= '<?xpacket end="w"?>';
12891  $out = '<< /Type /Metadata /Subtype /XML /Length '.strlen($xmp).' >> stream'."\n".$xmp."\n".'endstream'."\n".'endobj';
12892  // restore previous isunicode value
12893  $this->isunicode = $prev_isunicode;
12894  $this->encrypted = $prev_encrypted;
12895  $this->_out($out);
12896  return $oid;
12897  }
12898 
12904  protected function _putcatalog() {
12905  // put XMP
12906  $xmpobj = $this->_putXMP();
12907  // if required, add standard sRGB_IEC61966-2.1 blackscaled ICC colour profile
12908  if ($this->pdfa_mode OR $this->force_srgb) {
12909  $iccobj = $this->_newobj();
12910  $icc = file_get_contents(dirname(__FILE__).'/sRGB.icc');
12911  $filter = '';
12912  if ($this->compress) {
12913  $filter = ' /Filter /FlateDecode';
12914  $icc = gzcompress($icc);
12915  }
12916  $icc = $this->_getrawstream($icc);
12917  $this->_out('<</N 3 '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj');
12918  }
12919  // start catalog
12920  $oid = $this->_newobj();
12921  $out = '<< /Type /Catalog';
12922  $out .= ' /Version /'.$this->PDFVersion;
12923  //$out .= ' /Extensions <<>>';
12924  $out .= ' /Pages 1 0 R';
12925  //$out .= ' /PageLabels ' //...;
12926  $out .= ' /Names <<';
12927  if ((!$this->pdfa_mode) AND ((!empty($this->javascript)) OR (!empty($this->js_objects)))) {
12928  $out .= ' /JavaScript '.($this->n_js).' 0 R';
12929  }
12930  $out .= ' >>';
12931  if (!empty($this->dests)) {
12932  $out .= ' /Dests '.$this->n_dests.' 0 R';
12933  }
12934  $out .= $this->_putviewerpreferences();
12935  if (isset($this->LayoutMode) AND (!$this->empty_string($this->LayoutMode))) {
12936  $out .= ' /PageLayout /'.$this->LayoutMode;
12937  }
12938  if (isset($this->PageMode) AND (!$this->empty_string($this->PageMode))) {
12939  $out .= ' /PageMode /'.$this->PageMode;
12940  }
12941  if (count($this->outlines) > 0) {
12942  $out .= ' /Outlines '.$this->OutlineRoot.' 0 R';
12943  $out .= ' /PageMode /UseOutlines';
12944  }
12945  //$out .= ' /Threads []';
12946  if ($this->ZoomMode == 'fullpage') {
12947  $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /Fit]';
12948  } elseif ($this->ZoomMode == 'fullwidth') {
12949  $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /FitH null]';
12950  } elseif ($this->ZoomMode == 'real') {
12951  $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null 1]';
12952  } elseif (!is_string($this->ZoomMode)) {
12953  $out .= sprintf(' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null %F]', ($this->ZoomMode / 100));
12954  }
12955  //$out .= ' /AA <<>>';
12956  //$out .= ' /URI <<>>';
12957  $out .= ' /Metadata '.$xmpobj.' 0 R';
12958  //$out .= ' /StructTreeRoot <<>>';
12959  //$out .= ' /MarkInfo <<>>';
12960  if (isset($this->l['a_meta_language'])) {
12961  $out .= ' /Lang '.$this->_textstring($this->l['a_meta_language'], $oid);
12962  }
12963  //$out .= ' /SpiderInfo <<>>';
12964  // set OutputIntent to sRGB IEC61966-2.1 if required
12965  if ($this->pdfa_mode OR $this->force_srgb) {
12966  $out .= ' /OutputIntents [<<';
12967  $out .= ' /Type /OutputIntent';
12968  $out .= ' /S /GTS_PDFA1';
12969  $out .= ' /OutputCondition '.$this->_textstring('sRGB IEC61966-2.1', $oid);
12970  $out .= ' /OutputConditionIdentifier '.$this->_textstring('sRGB IEC61966-2.1', $oid);
12971  $out .= ' /RegistryName '.$this->_textstring('http://www.color.org', $oid);
12972  $out .= ' /Info '.$this->_textstring('sRGB IEC61966-2.1', $oid);
12973  $out .= ' /DestOutputProfile '.$iccobj.' 0 R';
12974  $out .= ' >>]';
12975  }
12976  //$out .= ' /PieceInfo <<>>';
12977  if (!empty($this->pdflayers)) {
12978  $lyrobjs = '';
12979  $lyrobjs_print = '';
12980  $lyrobjs_view = '';
12981  foreach ($this->pdflayers as $layer) {
12982  $lyrobjs .= ' '.$layer['objid'].' 0 R';
12983  if ($layer['print']) {
12984  $lyrobjs_print .= ' '.$layer['objid'].' 0 R';
12985  }
12986  if ($layer['view']) {
12987  $lyrobjs_view .= ' '.$layer['objid'].' 0 R';
12988  }
12989  }
12990  $out .= ' /OCProperties << /OCGs ['.$lyrobjs.']';
12991  $out .= ' /D <<';
12992  $out .= ' /Name '.$this->_textstring('Layers', $oid);
12993  $out .= ' /Creator '.$this->_textstring('TCPDF', $oid);
12994  $out .= ' /BaseState /ON';
12995  $out .= ' /ON ['.$lyrobjs_print.']';
12996  $out .= ' /OFF ['.$lyrobjs_view.']';
12997  $out .= ' /Intent /View';
12998  $out .= ' /AS [';
12999  $out .= ' << /Event /Print /OCGs ['.$lyrobjs.'] /Category [/Print] >>';
13000  $out .= ' << /Event /View /OCGs ['.$lyrobjs.'] /Category [/View] >>';
13001  $out .= ' ]';
13002  $out .= ' /Order ['.$lyrobjs.']';
13003  $out .= ' /ListMode /AllPages';
13004  //$out .= ' /RBGroups ['..']';
13005  //$out .= ' /Locked ['..']';
13006  $out .= ' >>';
13007  $out .= ' >>';
13008  }
13009  // AcroForm
13010  if (!empty($this->form_obj_id) OR ($this->sign AND isset($this->signature_data['cert_type']))) {
13011  $out .= ' /AcroForm <<';
13012  $objrefs = '';
13013  if ($this->sign AND isset($this->signature_data['cert_type'])) {
13014  // set reference for signature object
13015  $objrefs .= $this->sig_obj_id.' 0 R';
13016  }
13017  if (!empty($this->empty_signature_appearance)) {
13018  foreach ($this->empty_signature_appearance as $esa) {
13019  // set reference for empty signature objects
13020  $objrefs .= ' '.$esa['objid'].' 0 R';
13021  }
13022  }
13023  if (!empty($this->form_obj_id)) {
13024  foreach($this->form_obj_id as $objid) {
13025  $objrefs .= ' '.$objid.' 0 R';
13026  }
13027  }
13028  $out .= ' /Fields ['.$objrefs.']';
13029  // It's better to turn off this value and set the appearance stream for each annotation (/AP) to avoid conflicts with signature fields.
13030  $out .= ' /NeedAppearances false';
13031  if ($this->sign AND isset($this->signature_data['cert_type'])) {
13032  if ($this->signature_data['cert_type'] > 0) {
13033  $out .= ' /SigFlags 3';
13034  } else {
13035  $out .= ' /SigFlags 1';
13036  }
13037  }
13038  //$out .= ' /CO ';
13039  if (isset($this->annotation_fonts) AND !empty($this->annotation_fonts)) {
13040  $out .= ' /DR <<';
13041  $out .= ' /Font <<';
13042  foreach ($this->annotation_fonts as $fontkey => $fontid) {
13043  $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R';
13044  }
13045  $out .= ' >> >>';
13046  }
13047  $font = $this->getFontBuffer('helvetica');
13048  $out .= ' /DA (/F'.$font['i'].' 0 Tf 0 g)';
13049  $out .= ' /Q '.(($this->rtl)?'2':'0');
13050  //$out .= ' /XFA ';
13051  $out .= ' >>';
13052  // signatures
13053  if ($this->sign AND isset($this->signature_data['cert_type'])) {
13054  if ($this->signature_data['cert_type'] > 0) {
13055  $out .= ' /Perms << /DocMDP '.($this->sig_obj_id + 1).' 0 R >>';
13056  } else {
13057  $out .= ' /Perms << /UR3 '.($this->sig_obj_id + 1).' 0 R >>';
13058  }
13059  }
13060  }
13061  //$out .= ' /Legal <<>>';
13062  //$out .= ' /Requirements []';
13063  //$out .= ' /Collection <<>>';
13064  //$out .= ' /NeedsRendering true';
13065  $out .= ' >>';
13066  $out .= "\n".'endobj';
13067  $this->_out($out);
13068  return $oid;
13069  }
13070 
13078  protected function _putviewerpreferences() {
13079  $out = ' /ViewerPreferences <<';
13080  if ($this->rtl) {
13081  $out .= ' /Direction /R2L';
13082  } else {
13083  $out .= ' /Direction /L2R';
13084  }
13085  if (isset($this->viewer_preferences['HideToolbar']) AND ($this->viewer_preferences['HideToolbar'])) {
13086  $out .= ' /HideToolbar true';
13087  }
13088  if (isset($this->viewer_preferences['HideMenubar']) AND ($this->viewer_preferences['HideMenubar'])) {
13089  $out .= ' /HideMenubar true';
13090  }
13091  if (isset($this->viewer_preferences['HideWindowUI']) AND ($this->viewer_preferences['HideWindowUI'])) {
13092  $out .= ' /HideWindowUI true';
13093  }
13094  if (isset($this->viewer_preferences['FitWindow']) AND ($this->viewer_preferences['FitWindow'])) {
13095  $out .= ' /FitWindow true';
13096  }
13097  if (isset($this->viewer_preferences['CenterWindow']) AND ($this->viewer_preferences['CenterWindow'])) {
13098  $out .= ' /CenterWindow true';
13099  }
13100  if (isset($this->viewer_preferences['DisplayDocTitle']) AND ($this->viewer_preferences['DisplayDocTitle'])) {
13101  $out .= ' /DisplayDocTitle true';
13102  }
13103  if (isset($this->viewer_preferences['NonFullScreenPageMode'])) {
13104  $out .= ' /NonFullScreenPageMode /'.$this->viewer_preferences['NonFullScreenPageMode'];
13105  }
13106  if (isset($this->viewer_preferences['ViewArea'])) {
13107  $out .= ' /ViewArea /'.$this->viewer_preferences['ViewArea'];
13108  }
13109  if (isset($this->viewer_preferences['ViewClip'])) {
13110  $out .= ' /ViewClip /'.$this->viewer_preferences['ViewClip'];
13111  }
13112  if (isset($this->viewer_preferences['PrintArea'])) {
13113  $out .= ' /PrintArea /'.$this->viewer_preferences['PrintArea'];
13114  }
13115  if (isset($this->viewer_preferences['PrintClip'])) {
13116  $out .= ' /PrintClip /'.$this->viewer_preferences['PrintClip'];
13117  }
13118  if (isset($this->viewer_preferences['PrintScaling'])) {
13119  $out .= ' /PrintScaling /'.$this->viewer_preferences['PrintScaling'];
13120  }
13121  if (isset($this->viewer_preferences['Duplex']) AND (!$this->empty_string($this->viewer_preferences['Duplex']))) {
13122  $out .= ' /Duplex /'.$this->viewer_preferences['Duplex'];
13123  }
13124  if (isset($this->viewer_preferences['PickTrayByPDFSize'])) {
13125  if ($this->viewer_preferences['PickTrayByPDFSize']) {
13126  $out .= ' /PickTrayByPDFSize true';
13127  } else {
13128  $out .= ' /PickTrayByPDFSize false';
13129  }
13130  }
13131  if (isset($this->viewer_preferences['PrintPageRange'])) {
13132  $PrintPageRangeNum = '';
13133  foreach ($this->viewer_preferences['PrintPageRange'] as $k => $v) {
13134  $PrintPageRangeNum .= ' '.($v - 1).'';
13135  }
13136  $out .= ' /PrintPageRange ['.substr($PrintPageRangeNum,1).']';
13137  }
13138  if (isset($this->viewer_preferences['NumCopies'])) {
13139  $out .= ' /NumCopies '.intval($this->viewer_preferences['NumCopies']);
13140  }
13141  $out .= ' >>';
13142  return $out;
13143  }
13144 
13149  protected function _putheader() {
13150  $this->_out('%PDF-'.$this->PDFVersion);
13151  $this->_out('%'.chr(0xe2).chr(0xe3).chr(0xcf).chr(0xd3));
13152  }
13153 
13158  protected function _enddoc() {
13159  $this->state = 1;
13160  $this->_putheader();
13161  $this->_putpages();
13162  $this->_putresources();
13163  // empty signature fields
13164  if (!empty($this->empty_signature_appearance)) {
13165  foreach ($this->empty_signature_appearance as $key => $esa) {
13166  // widget annotation for empty signature
13167  $out = $this->_getobj($esa['objid'])."\n";
13168  $out .= '<< /Type /Annot';
13169  $out .= ' /Subtype /Widget';
13170  $out .= ' /Rect ['.$esa['rect'].']';
13171  $out .= ' /P '.$this->page_obj_id[($esa['page'])].' 0 R'; // link to signature appearance page
13172  $out .= ' /F 4';
13173  $out .= ' /FT /Sig';
13174  $signame = sprintf('Signature_%03d', ($key + 1));
13175  $out .= ' /T '.$this->_textstring($signame, $esa['objid']);
13176  $out .= ' /Ff 0';
13177  $out .= ' >>';
13178  $out .= "\n".'endobj';
13179  $this->_out($out);
13180  }
13181  }
13182  // Signature
13183  if ($this->sign AND isset($this->signature_data['cert_type'])) {
13184  // widget annotation for signature
13185  $out = $this->_getobj($this->sig_obj_id)."\n";
13186  $out .= '<< /Type /Annot';
13187  $out .= ' /Subtype /Widget';
13188  $out .= ' /Rect ['.$this->signature_appearance['rect'].']';
13189  $out .= ' /P '.$this->page_obj_id[($this->signature_appearance['page'])].' 0 R'; // link to signature appearance page
13190  $out .= ' /F 4';
13191  $out .= ' /FT /Sig';
13192  $out .= ' /T '.$this->_textstring('Signature_000', $this->sig_obj_id);
13193  $out .= ' /Ff 0';
13194  $out .= ' /V '.($this->sig_obj_id + 1).' 0 R';
13195  $out .= ' >>';
13196  $out .= "\n".'endobj';
13197  $this->_out($out);
13198  // signature
13199  $this->_putsignature();
13200  }
13201  // Info
13202  $objid_info = $this->_putinfo();
13203  // Catalog
13204  $objid_catalog = $this->_putcatalog();
13205  // Cross-ref
13206  $o = $this->bufferlen;
13207  // XREF section
13208  $this->_out('xref');
13209  $this->_out('0 '.($this->n + 1));
13210  $this->_out('0000000000 65535 f ');
13211  $freegen = ($this->n + 2);
13212  for ($i=1; $i <= $this->n; ++$i) {
13213  if (!isset($this->offsets[$i]) AND ($i > 1)) {
13214  $this->_out(sprintf('0000000000 %05d f ', $freegen));
13215  ++$freegen;
13216  } else {
13217  $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
13218  }
13219  }
13220  // TRAILER
13221  $out = 'trailer'."\n";
13222  $out .= '<<';
13223  $out .= ' /Size '.($this->n + 1);
13224  $out .= ' /Root '.$objid_catalog.' 0 R';
13225  $out .= ' /Info '.$objid_info.' 0 R';
13226  if ($this->encrypted) {
13227  $out .= ' /Encrypt '.$this->encryptdata['objid'].' 0 R';
13228  }
13229  $out .= ' /ID [ <'.$this->file_id.'> <'.$this->file_id.'> ]';
13230  $out .= ' >>';
13231  $this->_out($out);
13232  $this->_out('startxref');
13233  $this->_out($o);
13234  $this->_out('%%EOF');
13235  $this->state = 3; // end-of-doc
13236  if ($this->diskcache) {
13237  // remove temporary files used for images
13238  foreach ($this->imagekeys as $key) {
13239  // remove temporary files
13240  unlink($this->images[$key]);
13241  }
13242  foreach ($this->fontkeys as $key) {
13243  // remove temporary files
13244  unlink($this->fonts[$key]);
13245  }
13246  }
13247  }
13248 
13256  protected function _beginpage($orientation='', $format='') {
13257  ++$this->page;
13258  $this->pageobjects[$this->page] = array();
13259  $this->setPageBuffer($this->page, '');
13260  // initialize array for graphics tranformation positions inside a page buffer
13261  $this->transfmrk[$this->page] = array();
13262  $this->state = 2;
13263  if ($this->empty_string($orientation)) {
13264  if (isset($this->CurOrientation)) {
13265  $orientation = $this->CurOrientation;
13266  } elseif ($this->fwPt > $this->fhPt) {
13267  // landscape
13268  $orientation = 'L';
13269  } else {
13270  // portrait
13271  $orientation = 'P';
13272  }
13273  }
13274  if ($this->empty_string($format)) {
13275  $this->pagedim[$this->page] = $this->pagedim[($this->page - 1)];
13276  $this->setPageOrientation($orientation);
13277  } else {
13278  $this->setPageFormat($format, $orientation);
13279  }
13280  if ($this->rtl) {
13281  $this->x = $this->w - $this->rMargin;
13282  } else {
13283  $this->x = $this->lMargin;
13284  }
13285  $this->y = $this->tMargin;
13286  if (isset($this->newpagegroup[$this->page])) {
13287  // start a new group
13288  $this->currpagegroup = $this->newpagegroup[$this->page];
13289  $this->pagegroups[$this->currpagegroup] = 1;
13290  } elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) {
13291  ++$this->pagegroups[$this->currpagegroup];
13292  }
13293  }
13294 
13299  protected function _endpage() {
13300  $this->setVisibility('all');
13301  $this->state = 1;
13302  }
13303 
13309  protected function _newobj() {
13310  $this->_out($this->_getobj());
13311  return $this->n;
13312  }
13313 
13321  protected function _getobj($objid='') {
13322  if ($objid === '') {
13323  ++$this->n;
13324  $objid = $this->n;
13325  }
13326  $this->offsets[$objid] = $this->bufferlen;
13327  $this->pageobjects[$this->page][] = $objid;
13328  return $objid.' 0 obj';
13329  }
13330 
13338  protected function _dounderline($x, $y, $txt) {
13339  $w = $this->GetStringWidth($txt);
13340  return $this->_dounderlinew($x, $y, $w);
13341  }
13342 
13351  protected function _dounderlinew($x, $y, $w) {
13352  $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
13353  return sprintf('%F %F %F %F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew), $w * $this->k, $linew);
13354  }
13355 
13363  protected function _dolinethrough($x, $y, $txt) {
13364  $w = $this->GetStringWidth($txt);
13365  return $this->_dolinethroughw($x, $y, $w);
13366  }
13367 
13376  protected function _dolinethroughw($x, $y, $w) {
13377  $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
13378  return sprintf('%F %F %F %F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew + ($this->FontSizePt / 3)), $w * $this->k, $linew);
13379  }
13380 
13389  protected function _dooverline($x, $y, $txt) {
13390  $w = $this->GetStringWidth($txt);
13391  return $this->_dooverlinew($x, $y, $w);
13392  }
13393 
13402  protected function _dooverlinew($x, $y, $w) {
13403  $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
13404  return sprintf('%F %F %F %F re f', $x * $this->k, (($this->h - $y + $this->FontAscent) * $this->k) - $linew, $w * $this->k, $linew);
13405 
13406  }
13407 
13414  protected function _freadint($f) {
13415  $a = unpack('Ni', fread($f, 4));
13416  return $a['i'];
13417  }
13418 
13425  protected function _escape($s) {
13426  // the chr(13) substitution fixes the Bugs item #1421290.
13427  return strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r'));
13428  }
13429 
13437  protected function _datastring($s, $n=0) {
13438  if ($n == 0) {
13439  $n = $this->n;
13440  }
13441  $s = $this->_encrypt_data($n, $s);
13442  return '('. $this->_escape($s).')';
13443  }
13444 
13451  public function setDocCreationTimestamp($time) {
13452  if (is_string($time)) {
13453  $time = getTimestamp($time);
13454  }
13455  $this->doc_creation_timestamp = intval($time);
13456  }
13457 
13464  public function setDocModificationTimestamp($time) {
13465  if (is_string($time)) {
13466  $time = getTimestamp($time);
13467  }
13468  $this->doc_modification_timestamp = intval($time);
13469  }
13470 
13477  public function getDocCreationTimestamp() {
13478  return $this->doc_creation_timestamp;
13479  }
13480 
13487  public function getDocModificationTimestamp() {
13488  return $this->doc_modification_timestamp;
13489  }
13490 
13498  public function getTimestamp($date) {
13499  if (($date[0] == 'D') AND ($date[1] == ':')) {
13500  // remove date prefix if present
13501  $date = substr($date, 2);
13502  }
13503  return strtotime($date);
13504  }
13505 
13513  public function getFormattedDate($time) {
13514  return substr_replace(date('YmdHisO', intval($time)), '\'', (0 - 2), 0).'\'';
13515  }
13516 
13525  protected function _datestring($n=0, $timestamp=0) {
13526  if ((empty($timestamp)) OR ($timestamp < 0)) {
13527  $timestamp = $this->doc_creation_timestamp;
13528  }
13529  return $this->_datastring('D:'.$this->getFormattedDate($timestamp), $n);
13530  }
13531 
13539  protected function _textstring($s, $n=0) {
13540  if ($this->isunicode) {
13541  //Convert string to UTF-16BE
13542  $s = $this->UTF8ToUTF16BE($s, true);
13543  }
13544  return $this->_datastring($s, $n);
13545  }
13546 
13555  protected function _escapetext($s) {
13556  if ($this->isunicode) {
13557  if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
13558  $s = $this->UTF8ToLatin1($s);
13559  } else {
13560  //Convert string to UTF-16BE and reverse RTL language
13561  $s = $this->utf8StrRev($s, false, $this->tmprtl);
13562  }
13563  }
13564  return $this->_escape($s);
13565  }
13566 
13574  protected function _escapeXML($str) {
13575  $replaceTable = array("\0" => '', '&' => '&amp;', '<' => '&lt;', '>' => '&gt;');
13576  $str = strtr($str, $replaceTable);
13577  return $str;
13578  }
13579 
13588  protected function _getrawstream($s, $n=0) {
13589  if ($n <= 0) {
13590  // default to current object
13591  $n = $this->n;
13592  }
13593  return $this->_encrypt_data($n, $s);
13594  }
13595 
13603  protected function _getstream($s, $n=0) {
13604  return 'stream'."\n".$this->_getrawstream($s, $n)."\n".'endstream';
13605  }
13606 
13614  protected function _putstream($s, $n=0) {
13615  $this->_out($this->_getstream($s, $n));
13616  }
13617 
13623  protected function _out($s) {
13624  if ($this->state == 2) {
13625  if ($this->inxobj) {
13626  // we are inside an XObject template
13627  $this->xobjects[$this->xobjid]['outdata'] .= $s."\n";
13628  } elseif ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
13629  // puts data before page footer
13630  $pagebuff = $this->getPageBuffer($this->page);
13631  $page = substr($pagebuff, 0, -$this->footerlen[$this->page]);
13632  $footer = substr($pagebuff, -$this->footerlen[$this->page]);
13633  $this->setPageBuffer($this->page, $page.$s."\n".$footer);
13634  // update footer position
13635  $this->footerpos[$this->page] += strlen($s."\n");
13636  } else {
13637  // set page data
13638  $this->setPageBuffer($this->page, $s."\n", true);
13639  }
13640  } elseif ($this->state > 0) {
13641  // set general data
13642  $this->setBuffer($s."\n");
13643  }
13644  }
13645 
13680  protected function UTF8StringToArray($str) {
13681  // build a unique string key
13682  $strkey = md5($str);
13683  if (isset($this->cache_UTF8StringToArray[$strkey])) {
13684  // return cached value
13685  $chrarray = $this->cache_UTF8StringToArray[$strkey]['s'];
13686  if (!isset($this->cache_UTF8StringToArray[$strkey]['f'][$this->CurrentFont['fontkey']])) {
13687  if ($this->isunicode) {
13688  foreach ($chrarray as $chr) {
13689  // store this char for font subsetting
13690  $this->CurrentFont['subsetchars'][$chr] = true;
13691  }
13692  // update font subsetchars
13693  $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
13694  }
13695  $this->cache_UTF8StringToArray[$strkey]['f'][$this->CurrentFont['fontkey']] = true;
13696  }
13697  return $chrarray;
13698  }
13699  // check cache size
13700  if ($this->cache_size_UTF8StringToArray >= $this->cache_maxsize_UTF8StringToArray) {
13701  // remove first element
13702  array_shift($this->cache_UTF8StringToArray);
13703  }
13704  // new cache array for selected string
13705  $this->cache_UTF8StringToArray[$strkey] = array('s' => array(), 'f' => array());
13706  ++$this->cache_size_UTF8StringToArray;
13707  if (!$this->isunicode) {
13708  // split string into array of equivalent codes
13709  $strarr = array();
13710  $strlen = strlen($str);
13711  for ($i=0; $i < $strlen; ++$i) {
13712  $strarr[] = ord($str[$i]);
13713  }
13714  // insert new value on cache
13715  $this->cache_UTF8StringToArray[$strkey]['s'] = $strarr;
13716  $this->cache_UTF8StringToArray[$strkey]['f'][$this->CurrentFont['fontkey']] = true;
13717  return $strarr;
13718  }
13719  $unichar = -1; // last unicode char
13720  $unicode = array(); // array containing unicode values
13721  $bytes = array(); // array containing single character byte sequences
13722  $numbytes = 1; // number of octetc needed to represent the UTF-8 character
13723  $str .= ''; // force $str to be a string
13724  $length = strlen($str);
13725  for ($i = 0; $i < $length; ++$i) {
13726  $char = ord($str[$i]); // get one string character at time
13727  if (count($bytes) == 0) { // get starting octect
13728  if ($char <= 0x7F) {
13729  $unichar = $char; // use the character "as is" because is ASCII
13730  $numbytes = 1;
13731  } elseif (($char >> 0x05) == 0x06) { // 2 bytes character (0x06 = 110 BIN)
13732  $bytes[] = ($char - 0xC0) << 0x06;
13733  $numbytes = 2;
13734  } elseif (($char >> 0x04) == 0x0E) { // 3 bytes character (0x0E = 1110 BIN)
13735  $bytes[] = ($char - 0xE0) << 0x0C;
13736  $numbytes = 3;
13737  } elseif (($char >> 0x03) == 0x1E) { // 4 bytes character (0x1E = 11110 BIN)
13738  $bytes[] = ($char - 0xF0) << 0x12;
13739  $numbytes = 4;
13740  } else {
13741  // use replacement character for other invalid sequences
13742  $unichar = 0xFFFD;
13743  $bytes = array();
13744  $numbytes = 1;
13745  }
13746  } elseif (($char >> 0x06) == 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN
13747  $bytes[] = $char - 0x80;
13748  if (count($bytes) == $numbytes) {
13749  // compose UTF-8 bytes to a single unicode value
13750  $char = $bytes[0];
13751  for ($j = 1; $j < $numbytes; ++$j) {
13752  $char += ($bytes[$j] << (($numbytes - $j - 1) * 0x06));
13753  }
13754  if ((($char >= 0xD800) AND ($char <= 0xDFFF)) OR ($char >= 0x10FFFF)) {
13755  /* The definition of UTF-8 prohibits encoding character numbers between
13756  U+D800 and U+DFFF, which are reserved for use with the UTF-16
13757  encoding form (as surrogate pairs) and do not directly represent
13758  characters. */
13759  $unichar = 0xFFFD; // use replacement character
13760  } else {
13761  $unichar = $char; // add char to array
13762  }
13763  // reset data for next char
13764  $bytes = array();
13765  $numbytes = 1;
13766  }
13767  } else {
13768  // use replacement character for other invalid sequences
13769  $unichar = 0xFFFD;
13770  $bytes = array();
13771  $numbytes = 1;
13772  }
13773  if ($unichar >= 0) {
13774  // insert unicode value into array
13775  $unicode[] = $unichar;
13776  // store this char for font subsetting
13777  $this->CurrentFont['subsetchars'][$unichar] = true;
13778  $unichar = -1;
13779  }
13780  }
13781  // update font subsetchars
13782  $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
13783  // insert new value on cache
13784  $this->cache_UTF8StringToArray[$strkey]['s'] = $unicode;
13785  $this->cache_UTF8StringToArray[$strkey]['f'][$this->CurrentFont['fontkey']] = true;
13786  return $unicode;
13787  }
13788 
13799  protected function UTF8ToUTF16BE($str, $setbom=false) {
13800  if (!$this->isunicode) {
13801  return $str; // string is not in unicode
13802  }
13803  $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
13804  return $this->arrUTF8ToUTF16BE($unicode, $setbom);
13805  }
13806 
13815  protected function UTF8ToLatin1($str) {
13816  if (!$this->isunicode) {
13817  return $str; // string is not in unicode
13818  }
13819  $outstr = ''; // string to be returned
13820  $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
13821  foreach ($unicode as $char) {
13822  if ($char < 256) {
13823  $outstr .= chr($char);
13824  } elseif (array_key_exists($char, $this->unicode->uni_utf8tolatin)) {
13825  // map from UTF-8
13826  $outstr .= chr($this->unicode->uni_utf8tolatin[$char]);
13827  } elseif ($char == 0xFFFD) {
13828  // skip
13829  } else {
13830  $outstr .= '?';
13831  }
13832  }
13833  return $outstr;
13834  }
13835 
13844  protected function UTF8ArrToLatin1($unicode) {
13845  if ((!$this->isunicode) OR $this->isUnicodeFont()) {
13846  return $unicode;
13847  }
13848  $outarr = array(); // array to be returned
13849  foreach ($unicode as $char) {
13850  if ($char < 256) {
13851  $outarr[] = $char;
13852  } elseif (array_key_exists($char, $this->unicode->uni_utf8tolatin)) {
13853  // map from UTF-8
13854  $outarr[] = $this->unicode->uni_utf8tolatin[$char];
13855  } elseif ($char == 0xFFFD) {
13856  // skip
13857  } else {
13858  $outarr[] = 63; // '?' character
13859  }
13860  }
13861  return $outarr;
13862  }
13863 
13902  protected function arrUTF8ToUTF16BE($unicode, $setbom=false) {
13903  $outstr = ''; // string to be returned
13904  if ($setbom) {
13905  $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM)
13906  }
13907  foreach ($unicode as $char) {
13908  if ($char == 0x200b) {
13909  // skip Unicode Character 'ZERO WIDTH SPACE' (DEC:8203, U+200B)
13910  } elseif ($char == 0xFFFD) {
13911  $outstr .= "\xFF\xFD"; // replacement character
13912  } elseif ($char < 0x10000) {
13913  $outstr .= chr($char >> 0x08);
13914  $outstr .= chr($char & 0xFF);
13915  } else {
13916  $char -= 0x10000;
13917  $w1 = 0xD800 | ($char >> 0x0a);
13918  $w2 = 0xDC00 | ($char & 0x3FF);
13919  $outstr .= chr($w1 >> 0x08);
13920  $outstr .= chr($w1 & 0xFF);
13921  $outstr .= chr($w2 >> 0x08);
13922  $outstr .= chr($w2 & 0xFF);
13923  }
13924  }
13925  return $outstr;
13926  }
13927  // ====================================================
13928 
13935  public function setHeaderFont($font) {
13936  $this->header_font = $font;
13937  }
13938 
13945  public function getHeaderFont() {
13946  return $this->header_font;
13947  }
13948 
13955  public function setFooterFont($font) {
13956  $this->footer_font = $font;
13957  }
13958 
13965  public function getFooterFont() {
13966  return $this->footer_font;
13967  }
13968 
13975  public function setLanguageArray($language) {
13976  $this->l = $language;
13977  if (isset($this->l['a_meta_dir'])) {
13978  $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
13979  } else {
13980  $this->rtl = false;
13981  }
13982  }
13983 
13988  public function getPDFData() {
13989  if ($this->state < 3) {
13990  $this->Close();
13991  }
13992  return $this->buffer;
13993  }
13994 
14007  public function addHtmlLink($url, $name, $fill=false, $firstline=false, $color='', $style=-1, $firstblock=false) {
14008  if (!$this->empty_string($url) AND ($url[0] == '#') AND is_numeric($url[1])) {
14009  // convert url to internal link
14010  $lnkdata = explode(',', $url);
14011  if (isset($lnkdata[0])) {
14012  $page = intval(substr($lnkdata[0], 1));
14013  if (empty($page) OR ($page <= 0)) {
14014  $page = $this->page;
14015  }
14016  if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
14017  $lnky = floatval($lnkdata[1]);
14018  } else {
14019  $lnky = 0;
14020  }
14021  $url = $this->AddLink();
14022  $this->SetLink($url, $lnky, $page);
14023  }
14024  }
14025  // store current settings
14026  $prevcolor = $this->fgcolor;
14027  $prevstyle = $this->FontStyle;
14028  if (empty($color)) {
14029  $this->SetTextColorArray($this->htmlLinkColorArray);
14030  } else {
14031  $this->SetTextColorArray($color);
14032  }
14033  if ($style == -1) {
14034  $this->SetFont('', $this->FontStyle.$this->htmlLinkFontStyle);
14035  } else {
14036  $this->SetFont('', $this->FontStyle.$style);
14037  }
14038  $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline, $firstblock, 0);
14039  // restore settings
14040  $this->SetFont('', $prevstyle);
14041  $this->SetTextColorArray($prevcolor);
14042  return $ret;
14043  }
14044 
14052  public function convertHTMLColorToDec($hcolor='#FFFFFF', $defcol=array('R'=>128,'G'=>128,'B'=>128)) {
14053  $color = preg_replace('/[\s]*/', '', $hcolor); // remove extra spaces
14054  $color = strtolower($color);
14055  // check for javascript color array syntax
14056  if (strpos($color, '[') !== false) {
14057  if (preg_match('/[\[][\"\'](t|g|rgb|cmyk)[\"\'][\,]?([0-9\.]*)[\,]?([0-9\.]*)[\,]?([0-9\.]*)[\,]?([0-9\.]*)[\]]/', $color, $m) > 0) {
14058  $returncolor = array();
14059  switch ($m[1]) {
14060  case 'cmyk': {
14061  // RGB
14062  $returncolor['C'] = max(0, min(100, (floatval($m[2]) * 100)));
14063  $returncolor['M'] = max(0, min(100, (floatval($m[3]) * 100)));
14064  $returncolor['Y'] = max(0, min(100, (floatval($m[4]) * 100)));
14065  $returncolor['K'] = max(0, min(100, (floatval($m[5]) * 100)));
14066  break;
14067  }
14068  case 'rgb': {
14069  // RGB
14070  $returncolor['R'] = max(0, min(255, (floatval($m[2]) * 255)));
14071  $returncolor['G'] = max(0, min(255, (floatval($m[3]) * 255)));
14072  $returncolor['B'] = max(0, min(255, (floatval($m[4]) * 255)));
14073  break;
14074  }
14075  case 'g': {
14076  // grayscale
14077  $returncolor['G'] = max(0, min(255, (floatval($m[2]) * 255)));
14078  break;
14079  }
14080  case 't':
14081  default: {
14082  // transparent (empty array)
14083  break;
14084  }
14085  }
14086  return $returncolor;
14087  }
14088  } elseif (($dotpos = strpos($color, '.')) !== false) {
14089  // remove class parent (i.e.: color.red)
14090  $color = substr($color, ($dotpos + 1));
14091  if ($color == 'transparent') {
14092  // transparent (empty array)
14093  return array();
14094  }
14095  }
14096  if (strlen($color) == 0) {
14097  return $defcol;
14098  }
14099  // RGB ARRAY
14100  if (substr($color, 0, 3) == 'rgb') {
14101  $codes = substr($color, 4);
14102  $codes = str_replace(')', '', $codes);
14103  $returncolor = explode(',', $codes);
14104  foreach ($returncolor as $key => $val) {
14105  if (strpos($val, '%') > 0) {
14106  // percentage
14107  $returncolor[$key] = (255 * intval($val) / 100);
14108  } else {
14109  $returncolor[$key] = intval($val);
14110  }
14111  // normalize value
14112  $returncolor[$key] = max(0, min(255, $returncolor[$key]));
14113  }
14114  return $returncolor;
14115  }
14116  // CMYK ARRAY
14117  if (substr($color, 0, 4) == 'cmyk') {
14118  $codes = substr($color, 5);
14119  $codes = str_replace(')', '', $codes);
14120  $returncolor = explode(',', $codes);
14121  foreach ($returncolor as $key => $val) {
14122  if (strpos($val, '%') !== false) {
14123  // percentage
14124  $returncolor[$key] = (100 * intval($val) / 100);
14125  } else {
14126  $returncolor[$key] = intval($val);
14127  }
14128  // normalize value
14129  $returncolor[$key] = max(0, min(100, $returncolor[$key]));
14130  }
14131  return $returncolor;
14132  }
14133  if ($color{0} != '#') {
14134  // COLOR NAME
14135  if (isset($this->webcolor[$color])) {
14136  // web color
14137  $color_code = $this->webcolor[$color];
14138  } else {
14139  // spot color
14140  $returncolor = $this->getSpotColor($color);
14141  if ($returncolor === false) {
14142  $returncolor = $defcol;
14143  }
14144  return $returncolor;
14145  }
14146  } else {
14147  $color_code = substr($color, 1);
14148  }
14149  // HEXADECIMAL REPRESENTATION
14150  switch (strlen($color_code)) {
14151  case 3: {
14152  // 3-digit RGB hexadecimal representation
14153  $r = substr($color_code, 0, 1);
14154  $g = substr($color_code, 1, 1);
14155  $b = substr($color_code, 2, 1);
14156  $returncolor = array();
14157  $returncolor['R'] = max(0, min(255, hexdec($r.$r)));
14158  $returncolor['G'] = max(0, min(255, hexdec($g.$g)));
14159  $returncolor['B'] = max(0, min(255, hexdec($b.$b)));
14160  break;
14161  }
14162  case 6: {
14163  // 6-digit RGB hexadecimal representation
14164  $returncolor = array();
14165  $returncolor['R'] = max(0, min(255, hexdec(substr($color_code, 0, 2))));
14166  $returncolor['G'] = max(0, min(255, hexdec(substr($color_code, 2, 2))));
14167  $returncolor['B'] = max(0, min(255, hexdec(substr($color_code, 4, 2))));
14168  break;
14169  }
14170  case 8: {
14171  // 8-digit CMYK hexadecimal representation
14172  $returncolor = array();
14173  $returncolor['C'] = max(0, min(100, round(hexdec(substr($color_code, 0, 2)) / 2.55)));
14174  $returncolor['M'] = max(0, min(100, round(hexdec(substr($color_code, 2, 2)) / 2.55)));
14175  $returncolor['Y'] = max(0, min(100, round(hexdec(substr($color_code, 4, 2)) / 2.55)));
14176  $returncolor['K'] = max(0, min(100, round(hexdec(substr($color_code, 6, 2)) / 2.55)));
14177  break;
14178  }
14179  default: {
14180  $returncolor = $defcol;
14181  break;
14182  }
14183  }
14184  return $returncolor;
14185  }
14186 
14194  public function pixelsToUnits($px) {
14195  return ($px / ($this->imgscale * $this->k));
14196  }
14197 
14205  public function unhtmlentities($text_to_convert) {
14206  return @html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding);
14207  }
14208 
14209  // ENCRYPTION METHODS ----------------------------------
14210 
14219  protected function getRandomSeed($seed='') {
14220  $seed .= microtime();
14221  if (function_exists('openssl_random_pseudo_bytes') AND (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN')) {
14222  // this is not used on windows systems because it is very slow for a know bug
14223  $seed .= openssl_random_pseudo_bytes(512);
14224  } else {
14225  for ($i = 0; $i < 23; ++$i) {
14226  $seed .= uniqid('', true);
14227  }
14228  }
14229  $seed .= uniqid('', true);
14230  $seed .= rand();
14231  $seed .= @getmypid();
14232  $seed .= __FILE__;
14233  $seed .= $this->bufferlen;
14234  if (isset($_SERVER['REMOTE_ADDR'])) {
14235  $seed .= $_SERVER['REMOTE_ADDR'];
14236  }
14237  if (isset($_SERVER['HTTP_USER_AGENT'])) {
14238  $seed .= $_SERVER['HTTP_USER_AGENT'];
14239  }
14240  if (isset($_SERVER['HTTP_ACCEPT'])) {
14241  $seed .= $_SERVER['HTTP_ACCEPT'];
14242  }
14243  if (isset($_SERVER['HTTP_ACCEPT_ENCODING'])) {
14244  $seed .= $_SERVER['HTTP_ACCEPT_ENCODING'];
14245  }
14246  if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
14247  $seed .= $_SERVER['HTTP_ACCEPT_LANGUAGE'];
14248  }
14249  if (isset($_SERVER['HTTP_ACCEPT_CHARSET'])) {
14250  $seed .= $_SERVER['HTTP_ACCEPT_CHARSET'];
14251  }
14252  $seed .= rand();
14253  $seed .= uniqid('', true);
14254  $seed .= microtime();
14255  return $seed;
14256  }
14257 
14267  protected function _objectkey($n) {
14268  $objkey = $this->encryptdata['key'].pack('VXxx', $n);
14269  if ($this->encryptdata['mode'] == 2) { // AES-128
14270  // AES padding
14271  $objkey .= "\x73\x41\x6C\x54"; // sAlT
14272  }
14273  $objkey = substr($this->_md5_16($objkey), 0, (($this->encryptdata['Length'] / 8) + 5));
14274  $objkey = substr($objkey, 0, 16);
14275  return $objkey;
14276  }
14277 
14287  protected function _encrypt_data($n, $s) {
14288  if (!$this->encrypted) {
14289  return $s;
14290  }
14291  switch ($this->encryptdata['mode']) {
14292  case 0: // RC4-40
14293  case 1: { // RC4-128
14294  $s = $this->_RC4($this->_objectkey($n), $s);
14295  break;
14296  }
14297  case 2: { // AES-128
14298  $s = $this->_AES($this->_objectkey($n), $s);
14299  break;
14300  }
14301  case 3: { // AES-256
14302  $s = $this->_AES($this->encryptdata['key'], $s);
14303  break;
14304  }
14305  }
14306  return $s;
14307  }
14308 
14315  protected function _putencryption() {
14316  if (!$this->encrypted) {
14317  return;
14318  }
14319  $this->encryptdata['objid'] = $this->_newobj();
14320  $out = '<<';
14321  if (!isset($this->encryptdata['Filter']) OR empty($this->encryptdata['Filter'])) {
14322  $this->encryptdata['Filter'] = 'Standard';
14323  }
14324  $out .= ' /Filter /'.$this->encryptdata['Filter'];
14325  if (isset($this->encryptdata['SubFilter']) AND !empty($this->encryptdata['SubFilter'])) {
14326  $out .= ' /SubFilter /'.$this->encryptdata['SubFilter'];
14327  }
14328  if (!isset($this->encryptdata['V']) OR empty($this->encryptdata['V'])) {
14329  $this->encryptdata['V'] = 1;
14330  }
14331  // V is a code specifying the algorithm to be used in encrypting and decrypting the document
14332  $out .= ' /V '.$this->encryptdata['V'];
14333  if (isset($this->encryptdata['Length']) AND !empty($this->encryptdata['Length'])) {
14334  // The length of the encryption key, in bits. The value shall be a multiple of 8, in the range 40 to 256
14335  $out .= ' /Length '.$this->encryptdata['Length'];
14336  } else {
14337  $out .= ' /Length 40';
14338  }
14339  if ($this->encryptdata['V'] >= 4) {
14340  if (!isset($this->encryptdata['StmF']) OR empty($this->encryptdata['StmF'])) {
14341  $this->encryptdata['StmF'] = 'Identity';
14342  }
14343  if (!isset($this->encryptdata['StrF']) OR empty($this->encryptdata['StrF'])) {
14344  // The name of the crypt filter that shall be used when decrypting all strings in the document.
14345  $this->encryptdata['StrF'] = 'Identity';
14346  }
14347  // A dictionary whose keys shall be crypt filter names and whose values shall be the corresponding crypt filter dictionaries.
14348  if (isset($this->encryptdata['CF']) AND !empty($this->encryptdata['CF'])) {
14349  $out .= ' /CF <<';
14350  $out .= ' /'.$this->encryptdata['StmF'].' <<';
14351  $out .= ' /Type /CryptFilter';
14352  if (isset($this->encryptdata['CF']['CFM']) AND !empty($this->encryptdata['CF']['CFM'])) {
14353  // The method used
14354  $out .= ' /CFM /'.$this->encryptdata['CF']['CFM'];
14355  if ($this->encryptdata['pubkey']) {
14356  $out .= ' /Recipients [';
14357  foreach ($this->encryptdata['Recipients'] as $rec) {
14358  $out .= ' <'.$rec.'>';
14359  }
14360  $out .= ' ]';
14361  if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) {
14362  $out .= ' /EncryptMetadata false';
14363  } else {
14364  $out .= ' /EncryptMetadata true';
14365  }
14366  }
14367  } else {
14368  $out .= ' /CFM /None';
14369  }
14370  if (isset($this->encryptdata['CF']['AuthEvent']) AND !empty($this->encryptdata['CF']['AuthEvent'])) {
14371  // The event to be used to trigger the authorization that is required to access encryption keys used by this filter.
14372  $out .= ' /AuthEvent /'.$this->encryptdata['CF']['AuthEvent'];
14373  } else {
14374  $out .= ' /AuthEvent /DocOpen';
14375  }
14376  if (isset($this->encryptdata['CF']['Length']) AND !empty($this->encryptdata['CF']['Length'])) {
14377  // The bit length of the encryption key.
14378  $out .= ' /Length '.$this->encryptdata['CF']['Length'];
14379  }
14380  $out .= ' >> >>';
14381  }
14382  // The name of the crypt filter that shall be used by default when decrypting streams.
14383  $out .= ' /StmF /'.$this->encryptdata['StmF'];
14384  // The name of the crypt filter that shall be used when decrypting all strings in the document.
14385  $out .= ' /StrF /'.$this->encryptdata['StrF'];
14386  if (isset($this->encryptdata['EFF']) AND !empty($this->encryptdata['EFF'])) {
14387  // The name of the crypt filter that shall be used when encrypting embedded file streams that do not have their own crypt filter specifier.
14388  $out .= ' /EFF /'.$this->encryptdata[''];
14389  }
14390  }
14391  // Additional encryption dictionary entries for the standard security handler
14392  if ($this->encryptdata['pubkey']) {
14393  if (($this->encryptdata['V'] < 4) AND isset($this->encryptdata['Recipients']) AND !empty($this->encryptdata['Recipients'])) {
14394  $out .= ' /Recipients [';
14395  foreach ($this->encryptdata['Recipients'] as $rec) {
14396  $out .= ' <'.$rec.'>';
14397  }
14398  $out .= ' ]';
14399  }
14400  } else {
14401  $out .= ' /R';
14402  if ($this->encryptdata['V'] == 5) { // AES-256
14403  $out .= ' 5';
14404  $out .= ' /OE ('.$this->_escape($this->encryptdata['OE']).')';
14405  $out .= ' /UE ('.$this->_escape($this->encryptdata['UE']).')';
14406  $out .= ' /Perms ('.$this->_escape($this->encryptdata['perms']).')';
14407  } elseif ($this->encryptdata['V'] == 4) { // AES-128
14408  $out .= ' 4';
14409  } elseif ($this->encryptdata['V'] < 2) { // RC-40
14410  $out .= ' 2';
14411  } else { // RC-128
14412  $out .= ' 3';
14413  }
14414  $out .= ' /O ('.$this->_escape($this->encryptdata['O']).')';
14415  $out .= ' /U ('.$this->_escape($this->encryptdata['U']).')';
14416  $out .= ' /P '.$this->encryptdata['P'];
14417  if (isset($this->encryptdata['EncryptMetadata']) AND (!$this->encryptdata['EncryptMetadata'])) {
14418  $out .= ' /EncryptMetadata false';
14419  } else {
14420  $out .= ' /EncryptMetadata true';
14421  }
14422  }
14423  $out .= ' >>';
14424  $out .= "\n".'endobj';
14425  $this->_out($out);
14426  }
14427 
14438  protected function _RC4($key, $text) {
14439  if (function_exists('mcrypt_decrypt') AND ($out = @mcrypt_decrypt(MCRYPT_ARCFOUR, $key, $text, MCRYPT_MODE_STREAM, ''))) {
14440  // try to use mcrypt function if exist
14441  return $out;
14442  }
14443  if ($this->last_enc_key != $key) {
14444  $k = str_repeat($key, ((256 / strlen($key)) + 1));
14445  $rc4 = range(0, 255);
14446  $j = 0;
14447  for ($i = 0; $i < 256; ++$i) {
14448  $t = $rc4[$i];
14449  $j = ($j + $t + ord($k[$i])) % 256;
14450  $rc4[$i] = $rc4[$j];
14451  $rc4[$j] = $t;
14452  }
14453  $this->last_enc_key = $key;
14454  $this->last_enc_key_c = $rc4;
14455  } else {
14456  $rc4 = $this->last_enc_key_c;
14457  }
14458  $len = strlen($text);
14459  $a = 0;
14460  $b = 0;
14461  $out = '';
14462  for ($i = 0; $i < $len; ++$i) {
14463  $a = ($a + 1) % 256;
14464  $t = $rc4[$a];
14465  $b = ($b + $t) % 256;
14466  $rc4[$a] = $rc4[$b];
14467  $rc4[$b] = $t;
14468  $k = $rc4[($rc4[$a] + $rc4[$b]) % 256];
14469  $out .= chr(ord($text[$i]) ^ $k);
14470  }
14471  return $out;
14472  }
14473 
14484  protected function _AES($key, $text) {
14485  // padding (RFC 2898, PKCS #5: Password-Based Cryptography Specification Version 2.0)
14486  $padding = 16 - (strlen($text) % 16);
14487  $text .= str_repeat(chr($padding), $padding);
14488  $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_RAND);
14489  $text = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $text, MCRYPT_MODE_CBC, $iv);
14490  $text = $iv.$text;
14491  return $text;
14492  }
14493 
14502  protected function _md5_16($str) {
14503  return pack('H*', md5($str));
14504  }
14505 
14513  protected function _Uvalue() {
14514  if ($this->encryptdata['mode'] == 0) { // RC4-40
14515  return $this->_RC4($this->encryptdata['key'], $this->enc_padding);
14516  } elseif ($this->encryptdata['mode'] < 3) { // RC4-128, AES-128
14517  $tmp = $this->_md5_16($this->enc_padding.$this->encryptdata['fileid']);
14518  $enc = $this->_RC4($this->encryptdata['key'], $tmp);
14519  $len = strlen($tmp);
14520  for ($i = 1; $i <= 19; ++$i) {
14521  $ek = '';
14522  for ($j = 0; $j < $len; ++$j) {
14523  $ek .= chr(ord($this->encryptdata['key'][$j]) ^ $i);
14524  }
14525  $enc = $this->_RC4($ek, $enc);
14526  }
14527  $enc .= str_repeat("\x00", 16);
14528  return substr($enc, 0, 32);
14529  } elseif ($this->encryptdata['mode'] == 3) { // AES-256
14530  $seed = $this->_md5_16($this->getRandomSeed());
14531  // User Validation Salt
14532  $this->encryptdata['UVS'] = substr($seed, 0, 8);
14533  // User Key Salt
14534  $this->encryptdata['UKS'] = substr($seed, 8, 16);
14535  return hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UVS'], true).$this->encryptdata['UVS'].$this->encryptdata['UKS'];
14536  }
14537  }
14538 
14546  protected function _UEvalue() {
14547  $hashkey = hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UKS'], true);
14548  $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC));
14549  return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $hashkey, $this->encryptdata['key'], MCRYPT_MODE_CBC, $iv);
14550  }
14551 
14559  protected function _Ovalue() {
14560  if ($this->encryptdata['mode'] < 3) { // RC4-40, RC4-128, AES-128
14561  $tmp = $this->_md5_16($this->encryptdata['owner_password']);
14562  if ($this->encryptdata['mode'] > 0) {
14563  for ($i = 0; $i < 50; ++$i) {
14564  $tmp = $this->_md5_16($tmp);
14565  }
14566  }
14567  $owner_key = substr($tmp, 0, ($this->encryptdata['Length'] / 8));
14568  $enc = $this->_RC4($owner_key, $this->encryptdata['user_password']);
14569  if ($this->encryptdata['mode'] > 0) {
14570  $len = strlen($owner_key);
14571  for ($i = 1; $i <= 19; ++$i) {
14572  $ek = '';
14573  for ($j = 0; $j < $len; ++$j) {
14574  $ek .= chr(ord($owner_key[$j]) ^ $i);
14575  }
14576  $enc = $this->_RC4($ek, $enc);
14577  }
14578  }
14579  return $enc;
14580  } elseif ($this->encryptdata['mode'] == 3) { // AES-256
14581  $seed = $this->_md5_16($this->getRandomSeed());
14582  // Owner Validation Salt
14583  $this->encryptdata['OVS'] = substr($seed, 0, 8);
14584  // Owner Key Salt
14585  $this->encryptdata['OKS'] = substr($seed, 8, 16);
14586  return hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OVS'].$this->encryptdata['U'], true).$this->encryptdata['OVS'].$this->encryptdata['OKS'];
14587  }
14588  }
14589 
14597  protected function _OEvalue() {
14598  $hashkey = hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OKS'].$this->encryptdata['U'], true);
14599  $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC));
14600  return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $hashkey, $this->encryptdata['key'], MCRYPT_MODE_CBC, $iv);
14601  }
14602 
14611  protected function _fixAES256Password($password) {
14612  $psw = ''; // password to be returned
14613  $psw_array = $this->utf8Bidi($this->UTF8StringToArray($password), $password, $this->rtl);
14614  foreach ($psw_array as $c) {
14615  $psw .= $this->unichr($c);
14616  }
14617  return substr($psw, 0, 127);
14618  }
14619 
14626  protected function _generateencryptionkey() {
14627  $keybytelen = ($this->encryptdata['Length'] / 8);
14628  if (!$this->encryptdata['pubkey']) { // standard mode
14629  if ($this->encryptdata['mode'] == 3) { // AES-256
14630  // generate 256 bit random key
14631  $this->encryptdata['key'] = substr(hash('sha256', $this->getRandomSeed(), true), 0, $keybytelen);
14632  // truncate passwords
14633  $this->encryptdata['user_password'] = $this->_fixAES256Password($this->encryptdata['user_password']);
14634  $this->encryptdata['owner_password'] = $this->_fixAES256Password($this->encryptdata['owner_password']);
14635  // Compute U value
14636  $this->encryptdata['U'] = $this->_Uvalue();
14637  // Compute UE value
14638  $this->encryptdata['UE'] = $this->_UEvalue();
14639  // Compute O value
14640  $this->encryptdata['O'] = $this->_Ovalue();
14641  // Compute OE value
14642  $this->encryptdata['OE'] = $this->_OEvalue();
14643  // Compute P value
14644  $this->encryptdata['P'] = $this->encryptdata['protection'];
14645  // Computing the encryption dictionary's Perms (permissions) value
14646  $perms = $this->getEncPermissionsString($this->encryptdata['protection']); // bytes 0-3
14647  $perms .= chr(255).chr(255).chr(255).chr(255); // bytes 4-7
14648  if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) { // byte 8
14649  $perms .= 'F';
14650  } else {
14651  $perms .= 'T';
14652  }
14653  $perms .= 'adb'; // bytes 9-11
14654  $perms .= 'nick'; // bytes 12-15
14655  $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB));
14656  $this->encryptdata['perms'] = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $this->encryptdata['key'], $perms, MCRYPT_MODE_ECB, $iv);
14657  } else { // RC4-40, RC4-128, AES-128
14658  // Pad passwords
14659  $this->encryptdata['user_password'] = substr($this->encryptdata['user_password'].$this->enc_padding, 0, 32);
14660  $this->encryptdata['owner_password'] = substr($this->encryptdata['owner_password'].$this->enc_padding, 0, 32);
14661  // Compute O value
14662  $this->encryptdata['O'] = $this->_Ovalue();
14663  // get default permissions (reverse byte order)
14664  $permissions = $this->getEncPermissionsString($this->encryptdata['protection']);
14665  // Compute encryption key
14666  $tmp = $this->_md5_16($this->encryptdata['user_password'].$this->encryptdata['O'].$permissions.$this->encryptdata['fileid']);
14667  if ($this->encryptdata['mode'] > 0) {
14668  for ($i = 0; $i < 50; ++$i) {
14669  $tmp = $this->_md5_16(substr($tmp, 0, $keybytelen));
14670  }
14671  }
14672  $this->encryptdata['key'] = substr($tmp, 0, $keybytelen);
14673  // Compute U value
14674  $this->encryptdata['U'] = $this->_Uvalue();
14675  // Compute P value
14676  $this->encryptdata['P'] = $this->encryptdata['protection'];
14677  }
14678  } else { // Public-Key mode
14679  // random 20-byte seed
14680  $seed = sha1($this->getRandomSeed(), true);
14681  $recipient_bytes = '';
14682  foreach ($this->encryptdata['pubkeys'] as $pubkey) {
14683  // for each public certificate
14684  if (isset($pubkey['p'])) {
14685  $pkprotection = $this->getUserPermissionCode($pubkey['p'], $this->encryptdata['mode']);
14686  } else {
14687  $pkprotection = $this->encryptdata['protection'];
14688  }
14689  // get default permissions (reverse byte order)
14690  $pkpermissions = $this->getEncPermissionsString($pkprotection);
14691  // envelope data
14692  $envelope = $seed.$pkpermissions;
14693  // write the envelope data to a temporary file
14694  $tempkeyfile = tempnam(K_PATH_CACHE, 'tmpkey_');
14695  $f = fopen($tempkeyfile, 'wb');
14696  if (!$f) {
14697  $this->Error('Unable to create temporary key file: '.$tempkeyfile);
14698  }
14699  $envelope_length = strlen($envelope);
14700  fwrite($f, $envelope, $envelope_length);
14701  fclose($f);
14702  $tempencfile = tempnam(K_PATH_CACHE, 'tmpenc_');
14703  if (!openssl_pkcs7_encrypt($tempkeyfile, $tempencfile, $pubkey['c'], array(), PKCS7_BINARY | PKCS7_DETACHED)) {
14704  $this->Error('Unable to encrypt the file: '.$tempkeyfile);
14705  }
14706  unlink($tempkeyfile);
14707  // read encryption signature
14708  $signature = file_get_contents($tempencfile, false, null, $envelope_length);
14709  unlink($tempencfile);
14710  // extract signature
14711  $signature = substr($signature, strpos($signature, 'Content-Disposition'));
14712  $tmparr = explode("\n\n", $signature);
14713  $signature = trim($tmparr[1]);
14714  unset($tmparr);
14715  // decode signature
14716  $signature = base64_decode($signature);
14717  // convert signature to hex
14718  $hexsignature = current(unpack('H*', $signature));
14719  // store signature on recipients array
14720  $this->encryptdata['Recipients'][] = $hexsignature;
14721  // The bytes of each item in the Recipients array of PKCS#7 objects in the order in which they appear in the array
14722  $recipient_bytes .= $signature;
14723  }
14724  // calculate encryption key
14725  if ($this->encryptdata['mode'] == 3) { // AES-256
14726  $this->encryptdata['key'] = substr(hash('sha256', $seed.$recipient_bytes, true), 0, $keybytelen);
14727  } else { // RC4-40, RC4-128, AES-128
14728  $this->encryptdata['key'] = substr(sha1($seed.$recipient_bytes, true), 0, $keybytelen);
14729  }
14730  }
14731  }
14732 
14741  protected function getUserPermissionCode($permissions, $mode=0) {
14742  $options = array(
14743  'owner' => 2, // bit 2 -- inverted logic: cleared by default
14744  'print' => 4, // bit 3
14745  'modify' => 8, // bit 4
14746  'copy' => 16, // bit 5
14747  'annot-forms' => 32, // bit 6
14748  'fill-forms' => 256, // bit 9
14749  'extract' => 512, // bit 10
14750  'assemble' => 1024,// bit 11
14751  'print-high' => 2048 // bit 12
14752  );
14753  $protection = 2147422012; // 32 bit: (01111111 11111111 00001111 00111100)
14754  foreach ($permissions as $permission) {
14755  if (!isset($options[$permission])) {
14756  $this->Error('Incorrect permission: '.$permission);
14757  }
14758  if (($mode > 0) OR ($options[$permission] <= 32)) {
14759  // set only valid permissions
14760  if ($options[$permission] == 2) {
14761  // the logic for bit 2 is inverted (cleared by default)
14762  $protection += $options[$permission];
14763  } else {
14764  $protection -= $options[$permission];
14765  }
14766  }
14767  }
14768  return $protection;
14769  }
14770 
14785  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) {
14786  if ($this->pdfa_mode) {
14787  // encryption is not allowed in PDF/A mode
14788  return;
14789  }
14790  $this->encryptdata['protection'] = $this->getUserPermissionCode($permissions, $mode);
14791  if (($pubkeys !== null) AND (is_array($pubkeys))) {
14792  // public-key mode
14793  $this->encryptdata['pubkeys'] = $pubkeys;
14794  if ($mode == 0) {
14795  // public-Key Security requires at least 128 bit
14796  $mode = 1;
14797  }
14798  if (!function_exists('openssl_pkcs7_encrypt')) {
14799  $this->Error('Public-Key Security requires openssl library.');
14800  }
14801  // Set Public-Key filter (availabe are: Entrust.PPKEF, Adobe.PPKLite, Adobe.PubSec)
14802  $this->encryptdata['pubkey'] = true;
14803  $this->encryptdata['Filter'] = 'Adobe.PubSec';
14804  $this->encryptdata['StmF'] = 'DefaultCryptFilter';
14805  $this->encryptdata['StrF'] = 'DefaultCryptFilter';
14806  } else {
14807  // standard mode (password mode)
14808  $this->encryptdata['pubkey'] = false;
14809  $this->encryptdata['Filter'] = 'Standard';
14810  $this->encryptdata['StmF'] = 'StdCF';
14811  $this->encryptdata['StrF'] = 'StdCF';
14812  }
14813  if ($mode > 1) { // AES
14814  if (!extension_loaded('mcrypt')) {
14815  $this->Error('AES encryption requires mcrypt library (http://www.php.net/manual/en/mcrypt.requirements.php).');
14816  }
14817  if (mcrypt_get_cipher_name(MCRYPT_RIJNDAEL_128) === false) {
14818  $this->Error('AES encryption requires MCRYPT_RIJNDAEL_128 cypher.');
14819  }
14820  if (($mode == 3) AND !function_exists('hash')) {
14821  // the Hash extension requires no external libraries and is enabled by default as of PHP 5.1.2.
14822  $this->Error('AES 256 encryption requires HASH Message Digest Framework (http://www.php.net/manual/en/book.hash.php).');
14823  }
14824  }
14825  if ($owner_pass === null) {
14826  $owner_pass = md5($this->getRandomSeed());
14827  }
14828  $this->encryptdata['user_password'] = $user_pass;
14829  $this->encryptdata['owner_password'] = $owner_pass;
14830  $this->encryptdata['mode'] = $mode;
14831  switch ($mode) {
14832  case 0: { // RC4 40 bit
14833  $this->encryptdata['V'] = 1;
14834  $this->encryptdata['Length'] = 40;
14835  $this->encryptdata['CF']['CFM'] = 'V2';
14836  break;
14837  }
14838  case 1: { // RC4 128 bit
14839  $this->encryptdata['V'] = 2;
14840  $this->encryptdata['Length'] = 128;
14841  $this->encryptdata['CF']['CFM'] = 'V2';
14842  if ($this->encryptdata['pubkey']) {
14843  $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s4';
14844  $this->encryptdata['Recipients'] = array();
14845  }
14846  break;
14847  }
14848  case 2: { // AES 128 bit
14849  $this->encryptdata['V'] = 4;
14850  $this->encryptdata['Length'] = 128;
14851  $this->encryptdata['CF']['CFM'] = 'AESV2';
14852  $this->encryptdata['CF']['Length'] = 128;
14853  if ($this->encryptdata['pubkey']) {
14854  $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5';
14855  $this->encryptdata['Recipients'] = array();
14856  }
14857  break;
14858  }
14859  case 3: { // AES 256 bit
14860  $this->encryptdata['V'] = 5;
14861  $this->encryptdata['Length'] = 256;
14862  $this->encryptdata['CF']['CFM'] = 'AESV3';
14863  $this->encryptdata['CF']['Length'] = 256;
14864  if ($this->encryptdata['pubkey']) {
14865  $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5';
14866  $this->encryptdata['Recipients'] = array();
14867  }
14868  break;
14869  }
14870  }
14871  $this->encrypted = true;
14872  $this->encryptdata['fileid'] = $this->convertHexStringToString($this->file_id);
14873  $this->_generateencryptionkey();
14874  }
14875 
14884  protected function convertHexStringToString($bs) {
14885  $string = ''; // string to be returned
14886  $bslength = strlen($bs);
14887  if (($bslength % 2) != 0) {
14888  // padding
14889  $bs .= '0';
14890  ++$bslength;
14891  }
14892  for ($i = 0; $i < $bslength; $i += 2) {
14893  $string .= chr(hexdec($bs[$i].$bs[($i + 1)]));
14894  }
14895  return $string;
14896  }
14897 
14906  protected function convertStringToHexString($s) {
14907  $bs = '';
14908  $chars = preg_split('//', $s, -1, PREG_SPLIT_NO_EMPTY);
14909  foreach ($chars as $c) {
14910  $bs .= sprintf('%02s', dechex(ord($c)));
14911  }
14912  return $bs;
14913  }
14914 
14923  protected function getEncPermissionsString($protection) {
14924  $binprot = sprintf('%032b', $protection);
14925  $str = chr(bindec(substr($binprot, 24, 8)));
14926  $str .= chr(bindec(substr($binprot, 16, 8)));
14927  $str .= chr(bindec(substr($binprot, 8, 8)));
14928  $str .= chr(bindec(substr($binprot, 0, 8)));
14929  return $str;
14930  }
14931 
14932  // END OF ENCRYPTION FUNCTIONS -------------------------
14933 
14934  // START TRANSFORMATIONS SECTION -----------------------
14935 
14944  public function StartTransform() {
14945  if ($this->state != 2) {
14946  return;
14947  }
14948  $this->_out('q');
14949  if ($this->inxobj) {
14950  // we are inside an XObject template
14951  $this->xobjects[$this->xobjid]['transfmrk'][] = strlen($this->xobjects[$this->xobjid]['outdata']);
14952  } else {
14953  $this->transfmrk[$this->page][] = $this->pagelen[$this->page];
14954  }
14955  ++$this->transfmatrix_key;
14956  $this->transfmatrix[$this->transfmatrix_key] = array();
14957  }
14958 
14967  public function StopTransform() {
14968  if ($this->state != 2) {
14969  return;
14970  }
14971  $this->_out('Q');
14972  if (isset($this->transfmatrix[$this->transfmatrix_key])) {
14973  array_pop($this->transfmatrix[$this->transfmatrix_key]);
14974  --$this->transfmatrix_key;
14975  }
14976  if ($this->inxobj) {
14977  // we are inside an XObject template
14978  array_pop($this->xobjects[$this->xobjid]['transfmrk']);
14979  } else {
14980  array_pop($this->transfmrk[$this->page]);
14981  }
14982  }
14992  public function ScaleX($s_x, $x='', $y='') {
14993  $this->Scale($s_x, 100, $x, $y);
14994  }
14995 
15005  public function ScaleY($s_y, $x='', $y='') {
15006  $this->Scale(100, $s_y, $x, $y);
15007  }
15008 
15018  public function ScaleXY($s, $x='', $y='') {
15019  $this->Scale($s, $s, $x, $y);
15020  }
15021 
15032  public function Scale($s_x, $s_y, $x='', $y='') {
15033  if ($x === '') {
15034  $x = $this->x;
15035  }
15036  if ($y === '') {
15037  $y = $this->y;
15038  }
15039  if (($s_x == 0) OR ($s_y == 0)) {
15040  $this->Error('Please do not use values equal to zero for scaling');
15041  }
15042  $y = ($this->h - $y) * $this->k;
15043  $x *= $this->k;
15044  //calculate elements of transformation matrix
15045  $s_x /= 100;
15046  $s_y /= 100;
15047  $tm = array();
15048  $tm[0] = $s_x;
15049  $tm[1] = 0;
15050  $tm[2] = 0;
15051  $tm[3] = $s_y;
15052  $tm[4] = $x * (1 - $s_x);
15053  $tm[5] = $y * (1 - $s_y);
15054  //scale the coordinate system
15055  $this->Transform($tm);
15056  }
15057 
15065  public function MirrorH($x='') {
15066  $this->Scale(-100, 100, $x);
15067  }
15068 
15076  public function MirrorV($y='') {
15077  $this->Scale(100, -100, '', $y);
15078  }
15079 
15088  public function MirrorP($x='',$y='') {
15089  $this->Scale(-100, -100, $x, $y);
15090  }
15091 
15101  public function MirrorL($angle=0, $x='',$y='') {
15102  $this->Scale(-100, 100, $x, $y);
15103  $this->Rotate(-2*($angle-90), $x, $y);
15104  }
15105 
15113  public function TranslateX($t_x) {
15114  $this->Translate($t_x, 0);
15115  }
15116 
15124  public function TranslateY($t_y) {
15125  $this->Translate(0, $t_y);
15126  }
15127 
15136  public function Translate($t_x, $t_y) {
15137  //calculate elements of transformation matrix
15138  $tm = array();
15139  $tm[0] = 1;
15140  $tm[1] = 0;
15141  $tm[2] = 0;
15142  $tm[3] = 1;
15143  $tm[4] = $t_x * $this->k;
15144  $tm[5] = -$t_y * $this->k;
15145  //translate the coordinate system
15146  $this->Transform($tm);
15147  }
15148 
15158  public function Rotate($angle, $x='', $y='') {
15159  if ($x === '') {
15160  $x = $this->x;
15161  }
15162  if ($y === '') {
15163  $y = $this->y;
15164  }
15165  $y = ($this->h - $y) * $this->k;
15166  $x *= $this->k;
15167  //calculate elements of transformation matrix
15168  $tm = array();
15169  $tm[0] = cos(deg2rad($angle));
15170  $tm[1] = sin(deg2rad($angle));
15171  $tm[2] = -$tm[1];
15172  $tm[3] = $tm[0];
15173  $tm[4] = $x + ($tm[1] * $y) - ($tm[0] * $x);
15174  $tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x);
15175  //rotate the coordinate system around ($x,$y)
15176  $this->Transform($tm);
15177  }
15178 
15188  public function SkewX($angle_x, $x='', $y='') {
15189  $this->Skew($angle_x, 0, $x, $y);
15190  }
15191 
15201  public function SkewY($angle_y, $x='', $y='') {
15202  $this->Skew(0, $angle_y, $x, $y);
15203  }
15204 
15215  public function Skew($angle_x, $angle_y, $x='', $y='') {
15216  if ($x === '') {
15217  $x = $this->x;
15218  }
15219  if ($y === '') {
15220  $y = $this->y;
15221  }
15222  if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) {
15223  $this->Error('Please use values between -90 and +90 degrees for Skewing.');
15224  }
15225  $x *= $this->k;
15226  $y = ($this->h - $y) * $this->k;
15227  //calculate elements of transformation matrix
15228  $tm = array();
15229  $tm[0] = 1;
15230  $tm[1] = tan(deg2rad($angle_y));
15231  $tm[2] = tan(deg2rad($angle_x));
15232  $tm[3] = 1;
15233  $tm[4] = -$tm[2] * $y;
15234  $tm[5] = -$tm[1] * $x;
15235  //skew the coordinate system
15236  $this->Transform($tm);
15237  }
15238 
15246  protected function Transform($tm) {
15247  if ($this->state != 2) {
15248  return;
15249  }
15250  $this->_out(sprintf('%F %F %F %F %F %F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
15251  // add tranformation matrix
15252  $this->transfmatrix[$this->transfmatrix_key][] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]);
15253  // update transformation mark
15254  if ($this->inxobj) {
15255  // we are inside an XObject template
15256  if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
15257  $key = key($this->xobjects[$this->xobjid]['transfmrk']);
15258  $this->xobjects[$this->xobjid]['transfmrk'][$key] = strlen($this->xobjects[$this->xobjid]['outdata']);
15259  }
15260  } elseif (end($this->transfmrk[$this->page]) !== false) {
15261  $key = key($this->transfmrk[$this->page]);
15262  $this->transfmrk[$this->page][$key] = $this->pagelen[$this->page];
15263  }
15264  }
15265 
15266  // END TRANSFORMATIONS SECTION -------------------------
15267 
15268  // START GRAPHIC FUNCTIONS SECTION ---------------------
15269  // The following section is based on the code provided by David Hernandez Sanz
15270 
15278  public function SetLineWidth($width) {
15279  //Set line width
15280  $this->LineWidth = $width;
15281  $this->linestyleWidth = sprintf('%F w', ($width * $this->k));
15282  if ($this->state == 2) {
15283  $this->_out($this->linestyleWidth);
15284  }
15285  }
15286 
15294  public function GetLineWidth() {
15295  return $this->LineWidth;
15296  }
15297 
15321  public function SetLineStyle($style, $ret=false) {
15322  $s = ''; // string to be returned
15323  if (!is_array($style)) {
15324  return;
15325  }
15326  if (isset($style['width'])) {
15327  $this->LineWidth = $style['width'];
15328  $this->linestyleWidth = sprintf('%F w', ($style['width'] * $this->k));
15329  $s .= $this->linestyleWidth.' ';
15330  }
15331  if (isset($style['cap'])) {
15332  $ca = array('butt' => 0, 'round'=> 1, 'square' => 2);
15333  if (isset($ca[$style['cap']])) {
15334  $this->linestyleCap = $ca[$style['cap']].' J';
15335  $s .= $this->linestyleCap.' ';
15336  }
15337  }
15338  if (isset($style['join'])) {
15339  $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
15340  if (isset($ja[$style['join']])) {
15341  $this->linestyleJoin = $ja[$style['join']].' j';
15342  $s .= $this->linestyleJoin.' ';
15343  }
15344  }
15345  if (isset($style['dash'])) {
15346  $dash_string = '';
15347  if ($style['dash']) {
15348  if (preg_match('/^.+,/', $style['dash']) > 0) {
15349  $tab = explode(',', $style['dash']);
15350  } else {
15351  $tab = array($style['dash']);
15352  }
15353  $dash_string = '';
15354  foreach ($tab as $i => $v) {
15355  if ($i) {
15356  $dash_string .= ' ';
15357  }
15358  $dash_string .= sprintf('%F', $v);
15359  }
15360  }
15361  if (!isset($style['phase']) OR !$style['dash']) {
15362  $style['phase'] = 0;
15363  }
15364  $this->linestyleDash = sprintf('[%s] %F d', $dash_string, $style['phase']);
15365  $s .= $this->linestyleDash.' ';
15366  }
15367  if (isset($style['color'])) {
15368  $s .= $this->SetDrawColorArray($style['color'], true).' ';
15369  }
15370  if (!$ret AND ($this->state == 2)) {
15371  $this->_out($s);
15372  }
15373  return $s;
15374  }
15375 
15383  protected function _outPoint($x, $y) {
15384  if ($this->state == 2) {
15385  $this->_out(sprintf('%F %F m', ($x * $this->k), (($this->h - $y) * $this->k)));
15386  }
15387  }
15388 
15397  protected function _outLine($x, $y) {
15398  if ($this->state == 2) {
15399  $this->_out(sprintf('%F %F l', ($x * $this->k), (($this->h - $y) * $this->k)));
15400  }
15401  }
15402 
15413  protected function _outRect($x, $y, $w, $h, $op) {
15414  if ($this->state == 2) {
15415  $this->_out(sprintf('%F %F %F %F re %s', $x * $this->k, ($this->h - $y) * $this->k, $w * $this->k, -$h * $this->k, $op));
15416  }
15417  }
15418 
15431  protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) {
15432  if ($this->state == 2) {
15433  $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));
15434  }
15435  }
15436 
15447  protected function _outCurveV($x2, $y2, $x3, $y3) {
15448  if ($this->state == 2) {
15449  $this->_out(sprintf('%F %F %F %F v', $x2 * $this->k, ($this->h - $y2) * $this->k, $x3 * $this->k, ($this->h - $y3) * $this->k));
15450  }
15451  }
15452 
15463  protected function _outCurveY($x1, $y1, $x3, $y3) {
15464  if ($this->state == 2) {
15465  $this->_out(sprintf('%F %F %F %F y', $x1 * $this->k, ($this->h - $y1) * $this->k, $x3 * $this->k, ($this->h - $y3) * $this->k));
15466  }
15467  }
15468 
15480  public function Line($x1, $y1, $x2, $y2, $style=array()) {
15481  if ($this->state != 2) {
15482  return;
15483  }
15484  if (is_array($style)) {
15485  $this->SetLineStyle($style);
15486  }
15487  $this->_outPoint($x1, $y1);
15488  $this->_outLine($x2, $y2);
15489  $this->_out('S');
15490  }
15491 
15510  public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) {
15511  if ($this->state != 2) {
15512  return;
15513  }
15514  if (!(false === strpos($style, 'F')) AND !empty($fill_color)) {
15515  $this->SetFillColorArray($fill_color);
15516  }
15517  $op = $this->getPathPaintOperator($style);
15518  if ((!$border_style) OR (isset($border_style['all']))) {
15519  if (isset($border_style['all']) AND $border_style['all']) {
15520  $this->SetLineStyle($border_style['all']);
15521  $border_style = array();
15522  }
15523  }
15524  $this->_outRect($x, $y, $w, $h, $op);
15525  if ($border_style) {
15526  $border_style2 = array();
15527  foreach ($border_style as $line => $value) {
15528  $length = strlen($line);
15529  for ($i = 0; $i < $length; ++$i) {
15530  $border_style2[$line[$i]] = $value;
15531  }
15532  }
15533  $border_style = $border_style2;
15534  if (isset($border_style['L']) AND $border_style['L']) {
15535  $this->Line($x, $y, $x, $y + $h, $border_style['L']);
15536  }
15537  if (isset($border_style['T']) AND $border_style['T']) {
15538  $this->Line($x, $y, $x + $w, $y, $border_style['T']);
15539  }
15540  if (isset($border_style['R']) AND $border_style['R']) {
15541  $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style['R']);
15542  }
15543  if (isset($border_style['B']) AND $border_style['B']) {
15544  $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style['B']);
15545  }
15546  }
15547  }
15548 
15568  public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) {
15569  if ($this->state != 2) {
15570  return;
15571  }
15572  if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
15573  $this->SetFillColorArray($fill_color);
15574  }
15575  $op = $this->getPathPaintOperator($style);
15576  if ($line_style) {
15577  $this->SetLineStyle($line_style);
15578  }
15579  $this->_outPoint($x0, $y0);
15580  $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
15581  $this->_out($op);
15582  }
15583 
15598  public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) {
15599  if ($this->state != 2) {
15600  return;
15601  }
15602  if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
15603  $this->SetFillColorArray($fill_color);
15604  }
15605  $op = $this->getPathPaintOperator($style);
15606  if ($op == 'f') {
15607  $line_style = array();
15608  }
15609  if ($line_style) {
15610  $this->SetLineStyle($line_style);
15611  }
15612  $this->_outPoint($x0, $y0);
15613  foreach ($segments as $segment) {
15614  list($x1, $y1, $x2, $y2, $x3, $y3) = $segment;
15615  $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
15616  }
15617  $this->_out($op);
15618  }
15619 
15638  public function Ellipse($x0, $y0, $rx, $ry='', $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
15639  if ($this->state != 2) {
15640  return;
15641  }
15642  if ($this->empty_string($ry) OR ($ry == 0)) {
15643  $ry = $rx;
15644  }
15645  if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
15646  $this->SetFillColorArray($fill_color);
15647  }
15648  $op = $this->getPathPaintOperator($style);
15649  if ($op == 'f') {
15650  $line_style = array();
15651  }
15652  if ($line_style) {
15653  $this->SetLineStyle($line_style);
15654  }
15655  $this->_outellipticalarc($x0, $y0, $rx, $ry, $angle, $astart, $afinish, false, $nc, true, true, false);
15656  $this->_out($op);
15657  }
15658 
15679  protected function _outellipticalarc($xc, $yc, $rx, $ry, $xang=0, $angs=0, $angf=360, $pie=false, $nc=2, $startpoint=true, $ccw=true, $svg=false) {
15680  $k = $this->k;
15681  if ($nc < 2) {
15682  $nc = 2;
15683  }
15684  $xmin = 2147483647;
15685  $ymin = 2147483647;
15686  $xmax = 0;
15687  $ymax = 0;
15688  if ($pie) {
15689  // center of the arc
15690  $this->_outPoint($xc, $yc);
15691  }
15692  $xang = deg2rad((float) $xang);
15693  $angs = deg2rad((float) $angs);
15694  $angf = deg2rad((float) $angf);
15695  if ($svg) {
15696  $as = $angs;
15697  $af = $angf;
15698  } else {
15699  $as = atan2((sin($angs) / $ry), (cos($angs) / $rx));
15700  $af = atan2((sin($angf) / $ry), (cos($angf) / $rx));
15701  }
15702  if ($as < 0) {
15703  $as += (2 * M_PI);
15704  }
15705  if ($af < 0) {
15706  $af += (2 * M_PI);
15707  }
15708  if ($ccw AND ($as > $af)) {
15709  // reverse rotation
15710  $as -= (2 * M_PI);
15711  } elseif (!$ccw AND ($as < $af)) {
15712  // reverse rotation
15713  $af -= (2 * M_PI);
15714  }
15715  $total_angle = ($af - $as);
15716  if ($nc < 2) {
15717  $nc = 2;
15718  }
15719  // total arcs to draw
15720  $nc *= (2 * abs($total_angle) / M_PI);
15721  $nc = round($nc) + 1;
15722  // angle of each arc
15723  $arcang = ($total_angle / $nc);
15724  // center point in PDF coordinates
15725  $x0 = $xc;
15726  $y0 = ($this->h - $yc);
15727  // starting angle
15728  $ang = $as;
15729  $alpha = sin($arcang) * ((sqrt(4 + (3 * pow(tan(($arcang) / 2), 2))) - 1) / 3);
15730  $cos_xang = cos($xang);
15731  $sin_xang = sin($xang);
15732  $cos_ang = cos($ang);
15733  $sin_ang = sin($ang);
15734  // first arc point
15735  $px1 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
15736  $py1 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang);
15737  // first Bezier control point
15738  $qx1 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
15739  $qy1 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang)));
15740  if ($pie) {
15741  // line from center to arc starting point
15742  $this->_outLine($px1, $this->h - $py1);
15743  } elseif ($startpoint) {
15744  // arc starting point
15745  $this->_outPoint($px1, $this->h - $py1);
15746  }
15747  // draw arcs
15748  for ($i = 1; $i <= $nc; ++$i) {
15749  // starting angle
15750  $ang = $as + ($i * $arcang);
15751  if ($i == $nc) {
15752  $ang = $af;
15753  }
15754  $cos_ang = cos($ang);
15755  $sin_ang = sin($ang);
15756  // second arc point
15757  $px2 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
15758  $py2 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang);
15759  // second Bezier control point
15760  $qx2 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
15761  $qy2 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang)));
15762  // draw arc
15763  $cx1 = ($px1 + $qx1);
15764  $cy1 = ($this->h - ($py1 + $qy1));
15765  $cx2 = ($px2 - $qx2);
15766  $cy2 = ($this->h - ($py2 - $qy2));
15767  $cx3 = $px2;
15768  $cy3 = ($this->h - $py2);
15769  $this->_outCurve($cx1, $cy1, $cx2, $cy2, $cx3, $cy3);
15770  // get bounding box coordinates
15771  $xmin = min($xmin, $cx1, $cx2, $cx3);
15772  $ymin = min($ymin, $cy1, $cy2, $cy3);
15773  $xmax = max($xmax, $cx1, $cx2, $cx3);
15774  $ymax = max($ymax, $cy1, $cy2, $cy3);
15775  // move to next point
15776  $px1 = $px2;
15777  $py1 = $py2;
15778  $qx1 = $qx2;
15779  $qy1 = $qy2;
15780  }
15781  if ($pie) {
15782  $this->_outLine($xc, $yc);
15783  // get bounding box coordinates
15784  $xmin = min($xmin, $xc);
15785  $ymin = min($ymin, $yc);
15786  $xmax = max($xmax, $xc);
15787  $ymax = max($ymax, $yc);
15788  }
15789  return array($xmin, $ymin, $xmax, $ymax);
15790  }
15791 
15807  public function Circle($x0, $y0, $r, $angstr=0, $angend=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
15808  $this->Ellipse($x0, $y0, $r, $r, 0, $angstr, $angend, $style, $line_style, $fill_color, $nc);
15809  }
15810 
15825  public function PolyLine($p, $style='', $line_style=array(), $fill_color=array()) {
15826  $this->Polygon($p, $style, $line_style, $fill_color, false);
15827  }
15828 
15844  public function Polygon($p, $style='', $line_style=array(), $fill_color=array(), $closed=true) {
15845  if ($this->state != 2) {
15846  return;
15847  }
15848  $nc = count($p); // number of coordinates
15849  $np = $nc / 2; // number of points
15850  if ($closed) {
15851  // close polygon by adding the first 2 points at the end (one line)
15852  for ($i = 0; $i < 4; ++$i) {
15853  $p[$nc + $i] = $p[$i];
15854  }
15855  // copy style for the last added line
15856  if (isset($line_style[0])) {
15857  $line_style[$np] = $line_style[0];
15858  }
15859  $nc += 4;
15860  }
15861  if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
15862  $this->SetFillColorArray($fill_color);
15863  }
15864  $op = $this->getPathPaintOperator($style);
15865  if ($op == 'f') {
15866  $line_style = array();
15867  }
15868  $draw = true;
15869  if ($line_style) {
15870  if (isset($line_style['all'])) {
15871  $this->SetLineStyle($line_style['all']);
15872  } else {
15873  $draw = false;
15874  if ($op == 'B') {
15875  // draw fill
15876  $op = 'f';
15877  $this->_outPoint($p[0], $p[1]);
15878  for ($i = 2; $i < $nc; $i = $i + 2) {
15879  $this->_outLine($p[$i], $p[$i + 1]);
15880  }
15881  $this->_out($op);
15882  }
15883  // draw outline
15884  $this->_outPoint($p[0], $p[1]);
15885  for ($i = 2; $i < $nc; $i = $i + 2) {
15886  $line_num = ($i / 2) - 1;
15887  if (isset($line_style[$line_num])) {
15888  if ($line_style[$line_num] != 0) {
15889  if (is_array($line_style[$line_num])) {
15890  $this->_out('S');
15891  $this->SetLineStyle($line_style[$line_num]);
15892  $this->_outPoint($p[$i - 2], $p[$i - 1]);
15893  $this->_outLine($p[$i], $p[$i + 1]);
15894  $this->_out('S');
15895  $this->_outPoint($p[$i], $p[$i + 1]);
15896  } else {
15897  $this->_outLine($p[$i], $p[$i + 1]);
15898  }
15899  }
15900  } else {
15901  $this->_outLine($p[$i], $p[$i + 1]);
15902  }
15903  }
15904  $this->_out($op);
15905  }
15906  }
15907  if ($draw) {
15908  $this->_outPoint($p[0], $p[1]);
15909  for ($i = 2; $i < $nc; $i = $i + 2) {
15910  $this->_outLine($p[$i], $p[$i + 1]);
15911  }
15912  $this->_out($op);
15913  }
15914  }
15915 
15945  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()) {
15946  if (3 > $ns) {
15947  $ns = 3;
15948  }
15949  if ($draw_circle) {
15950  $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
15951  }
15952  $p = array();
15953  for ($i = 0; $i < $ns; ++$i) {
15954  $a = $angle + ($i * 360 / $ns);
15955  $a_rad = deg2rad((float) $a);
15956  $p[] = $x0 + ($r * sin($a_rad));
15957  $p[] = $y0 + ($r * cos($a_rad));
15958  }
15959  $this->Polygon($p, $style, $line_style, $fill_color);
15960  }
15961 
15993  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()) {
15994  if ($nv < 2) {
15995  $nv = 2;
15996  }
15997  if ($draw_circle) {
15998  $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
15999  }
16000  $p2 = array();
16001  $visited = array();
16002  for ($i = 0; $i < $nv; ++$i) {
16003  $a = $angle + ($i * 360 / $nv);
16004  $a_rad = deg2rad((float) $a);
16005  $p2[] = $x0 + ($r * sin($a_rad));
16006  $p2[] = $y0 + ($r * cos($a_rad));
16007  $visited[] = false;
16008  }
16009  $p = array();
16010  $i = 0;
16011  do {
16012  $p[] = $p2[$i * 2];
16013  $p[] = $p2[($i * 2) + 1];
16014  $visited[$i] = true;
16015  $i += $ng;
16016  $i %= $nv;
16017  } while (!$visited[$i]);
16018  $this->Polygon($p, $style, $line_style, $fill_color);
16019  }
16020 
16035  public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
16036  $this->RoundedRectXY($x, $y, $w, $h, $r, $r, $round_corner, $style, $border_style, $fill_color);
16037  }
16038 
16054  public function RoundedRectXY($x, $y, $w, $h, $rx, $ry, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
16055  if ($this->state != 2) {
16056  return;
16057  }
16058  if (($round_corner == '0000') OR (($rx == $ry) AND ($rx == 0))) {
16059  // Not rounded
16060  $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
16061  return;
16062  }
16063  // Rounded
16064  if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
16065  $this->SetFillColorArray($fill_color);
16066  }
16067  $op = $this->getPathPaintOperator($style);
16068  if ($op == 'f') {
16069  $border_style = array();
16070  }
16071  if ($border_style) {
16072  $this->SetLineStyle($border_style);
16073  }
16074  $MyArc = 4 / 3 * (sqrt(2) - 1);
16075  $this->_outPoint($x + $rx, $y);
16076  $xc = $x + $w - $rx;
16077  $yc = $y + $ry;
16078  $this->_outLine($xc, $y);
16079  if ($round_corner[0]) {
16080  $this->_outCurve($xc + ($rx * $MyArc), $yc - $ry, $xc + $rx, $yc - ($ry * $MyArc), $xc + $rx, $yc);
16081  } else {
16082  $this->_outLine($x + $w, $y);
16083  }
16084  $xc = $x + $w - $rx;
16085  $yc = $y + $h - $ry;
16086  $this->_outLine($x + $w, $yc);
16087  if ($round_corner[1]) {
16088  $this->_outCurve($xc + $rx, $yc + ($ry * $MyArc), $xc + ($rx * $MyArc), $yc + $ry, $xc, $yc + $ry);
16089  } else {
16090  $this->_outLine($x + $w, $y + $h);
16091  }
16092  $xc = $x + $rx;
16093  $yc = $y + $h - $ry;
16094  $this->_outLine($xc, $y + $h);
16095  if ($round_corner[2]) {
16096  $this->_outCurve($xc - ($rx * $MyArc), $yc + $ry, $xc - $rx, $yc + ($ry * $MyArc), $xc - $rx, $yc);
16097  } else {
16098  $this->_outLine($x, $y + $h);
16099  }
16100  $xc = $x + $rx;
16101  $yc = $y + $ry;
16102  $this->_outLine($x, $yc);
16103  if ($round_corner[3]) {
16104  $this->_outCurve($xc - $rx, $yc - ($ry * $MyArc), $xc - ($rx * $MyArc), $yc - $ry, $xc, $yc - $ry);
16105  } else {
16106  $this->_outLine($x, $y);
16107  $this->_outLine($x + $rx, $y);
16108  }
16109  $this->_out($op);
16110  }
16111 
16124  public function Arrow($x0, $y0, $x1, $y1, $head_style=0, $arm_size=5, $arm_angle=15) {
16125  // getting arrow direction angle
16126  // 0 deg angle is when both arms go along X axis. angle grows clockwise.
16127  $dir_angle = atan2(($y0 - $y1), ($x0 - $x1));
16128  if ($dir_angle < 0) {
16129  $dir_angle += (2 * M_PI);
16130  }
16131  $arm_angle = deg2rad($arm_angle);
16132  $sx1 = $x1;
16133  $sy1 = $y1;
16134  if ($head_style > 0) {
16135  // calculate the stopping point for the arrow shaft
16136  $sx1 = $x1 + (($arm_size - $this->LineWidth) * cos($dir_angle));
16137  $sy1 = $y1 + (($arm_size - $this->LineWidth) * sin($dir_angle));
16138  }
16139  // main arrow line / shaft
16140  $this->Line($x0, $y0, $sx1, $sy1);
16141  // left arrowhead arm tip
16142  $x2L = $x1 + ($arm_size * cos($dir_angle + $arm_angle));
16143  $y2L = $y1 + ($arm_size * sin($dir_angle + $arm_angle));
16144  // right arrowhead arm tip
16145  $x2R = $x1 + ($arm_size * cos($dir_angle - $arm_angle));
16146  $y2R = $y1 + ($arm_size * sin($dir_angle - $arm_angle));
16147  $mode = 'D';
16148  $style = array();
16149  switch ($head_style) {
16150  case 0: {
16151  // draw only arrowhead arms
16152  $mode = 'D';
16153  $style = array(1, 1, 0);
16154  break;
16155  }
16156  case 1: {
16157  // draw closed arrowhead, but no fill
16158  $mode = 'D';
16159  break;
16160  }
16161  case 2: {
16162  // closed and filled arrowhead
16163  $mode = 'DF';
16164  break;
16165  }
16166  case 3: {
16167  // filled arrowhead
16168  $mode = 'F';
16169  break;
16170  }
16171  }
16172  $this->Polygon(array($x2L, $y2L, $x1, $y1, $x2R, $y2R), $mode, $style, array());
16173  }
16174 
16175  // END GRAPHIC FUNCTIONS SECTION -----------------------
16176 
16177  // BIDIRECTIONAL TEXT SECTION --------------------------
16178 
16189  protected function utf8StrRev($str, $setbom=false, $forcertl=false) {
16190  return $this->utf8StrArrRev($this->UTF8StringToArray($str), $str, $setbom, $forcertl);
16191  }
16192 
16204  protected function utf8StrArrRev($arr, $str='', $setbom=false, $forcertl=false) {
16205  return $this->arrUTF8ToUTF16BE($this->utf8Bidi($arr, $str, $forcertl), $setbom);
16206  }
16207 
16218  protected function utf8Bidi($ta, $str='', $forcertl=false) {
16219  // paragraph embedding level
16220  $pel = 0;
16221  // max level
16222  $maxlevel = 0;
16223  if ($this->empty_string($str)) {
16224  // create string from array
16225  $str = $this->UTF8ArrSubString($ta);
16226  }
16227  // check if string contains arabic text
16228  if (preg_match($this->unicode->uni_RE_PATTERN_ARABIC, $str)) {
16229  $arabic = true;
16230  } else {
16231  $arabic = false;
16232  }
16233  // check if string contains RTL text
16234  if (!($forcertl OR $arabic OR preg_match($this->unicode->uni_RE_PATTERN_RTL, $str))) {
16235  return $ta;
16236  }
16237 
16238  // get number of chars
16239  $numchars = count($ta);
16240 
16241  if ($forcertl == 'R') {
16242  $pel = 1;
16243  } elseif ($forcertl == 'L') {
16244  $pel = 0;
16245  } else {
16246  // P2. In each paragraph, find the first character of type L, AL, or R.
16247  // P3. If a character is found in P2 and it is of type AL or R, then set the paragraph embedding level to one; otherwise, set it to zero.
16248  for ($i=0; $i < $numchars; ++$i) {
16249  $type = $this->unicode->uni_type[$ta[$i]];
16250  if ($type == 'L') {
16251  $pel = 0;
16252  break;
16253  } elseif (($type == 'AL') OR ($type == 'R')) {
16254  $pel = 1;
16255  break;
16256  }
16257  }
16258  }
16259 
16260  // Current Embedding Level
16261  $cel = $pel;
16262  // directional override status
16263  $dos = 'N';
16264  $remember = array();
16265  // start-of-level-run
16266  $sor = $pel % 2 ? 'R' : 'L';
16267  $eor = $sor;
16268 
16269  // Array of characters data
16270  $chardata = Array();
16271 
16272  // X1. Begin by setting the current embedding level to the paragraph embedding level. Set the directional override status to neutral. Process each character iteratively, applying rules X2 through X9. Only embedding levels from 0 to 61 are valid in this phase.
16273  // In the resolution of levels in rules I1 and I2, the maximum embedding level of 62 can be reached.
16274  for ($i=0; $i < $numchars; ++$i) {
16275  if ($ta[$i] == $this->unicode->uni_RLE) {
16276  // X2. With each RLE, compute the least greater odd embedding level.
16277  // a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to neutral.
16278  // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
16279  $next_level = $cel + ($cel % 2) + 1;
16280  if ($next_level < 62) {
16281  $remember[] = array('num' => $this->unicode->uni_RLE, 'cel' => $cel, 'dos' => $dos);
16282  $cel = $next_level;
16283  $dos = 'N';
16284  $sor = $eor;
16285  $eor = $cel % 2 ? 'R' : 'L';
16286  }
16287  } elseif ($ta[$i] == $this->unicode->uni_LRE) {
16288  // X3. With each LRE, compute the least greater even embedding level.
16289  // a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to neutral.
16290  // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
16291  $next_level = $cel + 2 - ($cel % 2);
16292  if ( $next_level < 62 ) {
16293  $remember[] = array('num' => $this->unicode->uni_LRE, 'cel' => $cel, 'dos' => $dos);
16294  $cel = $next_level;
16295  $dos = 'N';
16296  $sor = $eor;
16297  $eor = $cel % 2 ? 'R' : 'L';
16298  }
16299  } elseif ($ta[$i] == $this->unicode->uni_RLO) {
16300  // X4. With each RLO, compute the least greater odd embedding level.
16301  // a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to right-to-left.
16302  // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
16303  $next_level = $cel + ($cel % 2) + 1;
16304  if ($next_level < 62) {
16305  $remember[] = array('num' => $this->unicode->uni_RLO, 'cel' => $cel, 'dos' => $dos);
16306  $cel = $next_level;
16307  $dos = 'R';
16308  $sor = $eor;
16309  $eor = $cel % 2 ? 'R' : 'L';
16310  }
16311  } elseif ($ta[$i] == $this->unicode->uni_LRO) {
16312  // X5. With each LRO, compute the least greater even embedding level.
16313  // a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to left-to-right.
16314  // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
16315  $next_level = $cel + 2 - ($cel % 2);
16316  if ( $next_level < 62 ) {
16317  $remember[] = array('num' => $this->unicode->uni_LRO, 'cel' => $cel, 'dos' => $dos);
16318  $cel = $next_level;
16319  $dos = 'L';
16320  $sor = $eor;
16321  $eor = $cel % 2 ? 'R' : 'L';
16322  }
16323  } elseif ($ta[$i] == $this->unicode->uni_PDF) {
16324  // X7. With each PDF, determine the matching embedding or override code. If there was a valid matching code, restore (pop) the last remembered (pushed) embedding level and directional override.
16325  if (count($remember)) {
16326  $last = count($remember ) - 1;
16327  if (($remember[$last]['num'] == $this->unicode->uni_RLE) OR
16328  ($remember[$last]['num'] == $this->unicode->uni_LRE) OR
16329  ($remember[$last]['num'] == $this->unicode->uni_RLO) OR
16330  ($remember[$last]['num'] == $this->unicode->uni_LRO)) {
16331  $match = array_pop($remember);
16332  $cel = $match['cel'];
16333  $dos = $match['dos'];
16334  $sor = $eor;
16335  $eor = ($cel > $match['cel'] ? $cel : $match['cel']) % 2 ? 'R' : 'L';
16336  }
16337  }
16338  } elseif (($ta[$i] != $this->unicode->uni_RLE) AND
16339  ($ta[$i] != $this->unicode->uni_LRE) AND
16340  ($ta[$i] != $this->unicode->uni_RLO) AND
16341  ($ta[$i] != $this->unicode->uni_LRO) AND
16342  ($ta[$i] != $this->unicode->uni_PDF)) {
16343  // X6. For all types besides RLE, LRE, RLO, LRO, and PDF:
16344  // a. Set the level of the current character to the current embedding level.
16345  // b. Whenever the directional override status is not neutral, reset the current character type to the directional override status.
16346  if ($dos != 'N') {
16347  $chardir = $dos;
16348  } else {
16349  if (isset($this->unicode->uni_type[$ta[$i]])) {
16350  $chardir = $this->unicode->uni_type[$ta[$i]];
16351  } else {
16352  $chardir = 'L';
16353  }
16354  }
16355  // stores string characters and other information
16356  $chardata[] = array('char' => $ta[$i], 'level' => $cel, 'type' => $chardir, 'sor' => $sor, 'eor' => $eor);
16357  }
16358  } // end for each char
16359 
16360  // X8. All explicit directional embeddings and overrides are completely terminated at the end of each paragraph. Paragraph separators are not included in the embedding.
16361  // X9. Remove all RLE, LRE, RLO, LRO, PDF, and BN codes.
16362  // X10. The remaining rules are applied to each run of characters at the same level. For each run, determine the start-of-level-run (sor) and end-of-level-run (eor) type, either L or R. This depends on the higher of the two levels on either side of the boundary (at the start or end of the paragraph, the level of the 'other' run is the base embedding level). If the higher level is odd, the type is R; otherwise, it is L.
16363 
16364  // 3.3.3 Resolving Weak Types
16365  // Weak types are now resolved one level run at a time. At level run boundaries where the type of the character on the other side of the boundary is required, the type assigned to sor or eor is used.
16366  // Nonspacing marks are now resolved based on the previous characters.
16367  $numchars = count($chardata);
16368 
16369  // W1. Examine each nonspacing mark (NSM) in the level run, and change the type of the NSM to the type of the previous character. If the NSM is at the start of the level run, it will get the type of sor.
16370  $prevlevel = -1; // track level changes
16371  $levcount = 0; // counts consecutive chars at the same level
16372  for ($i=0; $i < $numchars; ++$i) {
16373  if ($chardata[$i]['type'] == 'NSM') {
16374  if ($levcount) {
16375  $chardata[$i]['type'] = $chardata[$i]['sor'];
16376  } elseif ($i > 0) {
16377  $chardata[$i]['type'] = $chardata[($i-1)]['type'];
16378  }
16379  }
16380  if ($chardata[$i]['level'] != $prevlevel) {
16381  $levcount = 0;
16382  } else {
16383  ++$levcount;
16384  }
16385  $prevlevel = $chardata[$i]['level'];
16386  }
16387 
16388  // W2. Search backward from each instance of a European number until the first strong type (R, L, AL, or sor) is found. If an AL is found, change the type of the European number to Arabic number.
16389  $prevlevel = -1;
16390  $levcount = 0;
16391  for ($i=0; $i < $numchars; ++$i) {
16392  if ($chardata[$i]['char'] == 'EN') {
16393  for ($j=$levcount; $j >= 0; $j--) {
16394  if ($chardata[$j]['type'] == 'AL') {
16395  $chardata[$i]['type'] = 'AN';
16396  } elseif (($chardata[$j]['type'] == 'L') OR ($chardata[$j]['type'] == 'R')) {
16397  break;
16398  }
16399  }
16400  }
16401  if ($chardata[$i]['level'] != $prevlevel) {
16402  $levcount = 0;
16403  } else {
16404  ++$levcount;
16405  }
16406  $prevlevel = $chardata[$i]['level'];
16407  }
16408 
16409  // W3. Change all ALs to R.
16410  for ($i=0; $i < $numchars; ++$i) {
16411  if ($chardata[$i]['type'] == 'AL') {
16412  $chardata[$i]['type'] = 'R';
16413  }
16414  }
16415 
16416  // W4. A single European separator between two European numbers changes to a European number. A single common separator between two numbers of the same type changes to that type.
16417  $prevlevel = -1;
16418  $levcount = 0;
16419  for ($i=0; $i < $numchars; ++$i) {
16420  if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
16421  if (($chardata[$i]['type'] == 'ES') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
16422  $chardata[$i]['type'] = 'EN';
16423  } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
16424  $chardata[$i]['type'] = 'EN';
16425  } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'AN') AND ($chardata[($i+1)]['type'] == 'AN')) {
16426  $chardata[$i]['type'] = 'AN';
16427  }
16428  }
16429  if ($chardata[$i]['level'] != $prevlevel) {
16430  $levcount = 0;
16431  } else {
16432  ++$levcount;
16433  }
16434  $prevlevel = $chardata[$i]['level'];
16435  }
16436 
16437  // W5. A sequence of European terminators adjacent to European numbers changes to all European numbers.
16438  $prevlevel = -1;
16439  $levcount = 0;
16440  for ($i=0; $i < $numchars; ++$i) {
16441  if ($chardata[$i]['type'] == 'ET') {
16442  if (($levcount > 0) AND ($chardata[($i-1)]['type'] == 'EN')) {
16443  $chardata[$i]['type'] = 'EN';
16444  } else {
16445  $j = $i+1;
16446  while (($j < $numchars) AND ($chardata[$j]['level'] == $prevlevel)) {
16447  if ($chardata[$j]['type'] == 'EN') {
16448  $chardata[$i]['type'] = 'EN';
16449  break;
16450  } elseif ($chardata[$j]['type'] != 'ET') {
16451  break;
16452  }
16453  ++$j;
16454  }
16455  }
16456  }
16457  if ($chardata[$i]['level'] != $prevlevel) {
16458  $levcount = 0;
16459  } else {
16460  ++$levcount;
16461  }
16462  $prevlevel = $chardata[$i]['level'];
16463  }
16464 
16465  // W6. Otherwise, separators and terminators change to Other Neutral.
16466  $prevlevel = -1;
16467  $levcount = 0;
16468  for ($i=0; $i < $numchars; ++$i) {
16469  if (($chardata[$i]['type'] == 'ET') OR ($chardata[$i]['type'] == 'ES') OR ($chardata[$i]['type'] == 'CS')) {
16470  $chardata[$i]['type'] = 'ON';
16471  }
16472  if ($chardata[$i]['level'] != $prevlevel) {
16473  $levcount = 0;
16474  } else {
16475  ++$levcount;
16476  }
16477  $prevlevel = $chardata[$i]['level'];
16478  }
16479 
16480  //W7. Search backward from each instance of a European number until the first strong type (R, L, or sor) is found. If an L is found, then change the type of the European number to L.
16481  $prevlevel = -1;
16482  $levcount = 0;
16483  for ($i=0; $i < $numchars; ++$i) {
16484  if ($chardata[$i]['char'] == 'EN') {
16485  for ($j=$levcount; $j >= 0; $j--) {
16486  if ($chardata[$j]['type'] == 'L') {
16487  $chardata[$i]['type'] = 'L';
16488  } elseif ($chardata[$j]['type'] == 'R') {
16489  break;
16490  }
16491  }
16492  }
16493  if ($chardata[$i]['level'] != $prevlevel) {
16494  $levcount = 0;
16495  } else {
16496  ++$levcount;
16497  }
16498  $prevlevel = $chardata[$i]['level'];
16499  }
16500 
16501  // N1. A sequence of neutrals takes the direction of the surrounding strong text if the text on both sides has the same direction. European and Arabic numbers act as if they were R in terms of their influence on neutrals. Start-of-level-run (sor) and end-of-level-run (eor) are used at level run boundaries.
16502  $prevlevel = -1;
16503  $levcount = 0;
16504  for ($i=0; $i < $numchars; ++$i) {
16505  if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
16506  if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
16507  $chardata[$i]['type'] = 'L';
16508  } elseif (($chardata[$i]['type'] == 'N') AND
16509  (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
16510  (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
16511  $chardata[$i]['type'] = 'R';
16512  } elseif ($chardata[$i]['type'] == 'N') {
16513  // N2. Any remaining neutrals take the embedding direction
16514  $chardata[$i]['type'] = $chardata[$i]['sor'];
16515  }
16516  } elseif (($levcount == 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
16517  // first char
16518  if (($chardata[$i]['type'] == 'N') AND ($chardata[$i]['sor'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
16519  $chardata[$i]['type'] = 'L';
16520  } elseif (($chardata[$i]['type'] == 'N') AND
16521  (($chardata[$i]['sor'] == 'R') OR ($chardata[$i]['sor'] == 'EN') OR ($chardata[$i]['sor'] == 'AN')) AND
16522  (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
16523  $chardata[$i]['type'] = 'R';
16524  } elseif ($chardata[$i]['type'] == 'N') {
16525  // N2. Any remaining neutrals take the embedding direction
16526  $chardata[$i]['type'] = $chardata[$i]['sor'];
16527  }
16528  } elseif (($levcount > 0) AND ((($i+1) == $numchars) OR (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] != $prevlevel))) {
16529  //last char
16530  if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[$i]['eor'] == 'L')) {
16531  $chardata[$i]['type'] = 'L';
16532  } elseif (($chardata[$i]['type'] == 'N') AND
16533  (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
16534  (($chardata[$i]['eor'] == 'R') OR ($chardata[$i]['eor'] == 'EN') OR ($chardata[$i]['eor'] == 'AN'))) {
16535  $chardata[$i]['type'] = 'R';
16536  } elseif ($chardata[$i]['type'] == 'N') {
16537  // N2. Any remaining neutrals take the embedding direction
16538  $chardata[$i]['type'] = $chardata[$i]['sor'];
16539  }
16540  } elseif ($chardata[$i]['type'] == 'N') {
16541  // N2. Any remaining neutrals take the embedding direction
16542  $chardata[$i]['type'] = $chardata[$i]['sor'];
16543  }
16544  if ($chardata[$i]['level'] != $prevlevel) {
16545  $levcount = 0;
16546  } else {
16547  ++$levcount;
16548  }
16549  $prevlevel = $chardata[$i]['level'];
16550  }
16551 
16552  // I1. For all characters with an even (left-to-right) embedding direction, those of type R go up one level and those of type AN or EN go up two levels.
16553  // I2. For all characters with an odd (right-to-left) embedding direction, those of type L, EN or AN go up one level.
16554  for ($i=0; $i < $numchars; ++$i) {
16555  $odd = $chardata[$i]['level'] % 2;
16556  if ($odd) {
16557  if (($chardata[$i]['type'] == 'L') OR ($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')) {
16558  $chardata[$i]['level'] += 1;
16559  }
16560  } else {
16561  if ($chardata[$i]['type'] == 'R') {
16562  $chardata[$i]['level'] += 1;
16563  } elseif (($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')) {
16564  $chardata[$i]['level'] += 2;
16565  }
16566  }
16567  $maxlevel = max($chardata[$i]['level'],$maxlevel);
16568  }
16569 
16570  // L1. On each line, reset the embedding level of the following characters to the paragraph embedding level:
16571  // 1. Segment separators,
16572  // 2. Paragraph separators,
16573  // 3. Any sequence of whitespace characters preceding a segment separator or paragraph separator, and
16574  // 4. Any sequence of white space characters at the end of the line.
16575  for ($i=0; $i < $numchars; ++$i) {
16576  if (($chardata[$i]['type'] == 'B') OR ($chardata[$i]['type'] == 'S')) {
16577  $chardata[$i]['level'] = $pel;
16578  } elseif ($chardata[$i]['type'] == 'WS') {
16579  $j = $i+1;
16580  while ($j < $numchars) {
16581  if ((($chardata[$j]['type'] == 'B') OR ($chardata[$j]['type'] == 'S')) OR
16582  (($j == ($numchars-1)) AND ($chardata[$j]['type'] == 'WS'))) {
16583  $chardata[$i]['level'] = $pel;
16584  break;
16585  } elseif ($chardata[$j]['type'] != 'WS') {
16586  break;
16587  }
16588  ++$j;
16589  }
16590  }
16591  }
16592 
16593  // Arabic Shaping
16594  // Cursively connected scripts, such as Arabic or Syriac, require the selection of positional character shapes that depend on adjacent characters. Shaping is logically applied after the Bidirectional Algorithm is used and is limited to characters within the same directional run.
16595  if ($arabic) {
16596  $endedletter = array(1569,1570,1571,1572,1573,1575,1577,1583,1584,1585,1586,1608,1688);
16597  $alfletter = array(1570,1571,1573,1575);
16598  $chardata2 = $chardata;
16599  $laaletter = false;
16600  $charAL = array();
16601  $x = 0;
16602  for ($i=0; $i < $numchars; ++$i) {
16603  if (($this->unicode->uni_type[$chardata[$i]['char']] == 'AL') OR ($chardata[$i]['char'] == 32) OR ($chardata[$i]['char'] == 8204)) {
16604  $charAL[$x] = $chardata[$i];
16605  $charAL[$x]['i'] = $i;
16606  $chardata[$i]['x'] = $x;
16607  ++$x;
16608  }
16609  }
16610  $numAL = $x;
16611  for ($i=0; $i < $numchars; ++$i) {
16612  $thischar = $chardata[$i];
16613  if ($i > 0) {
16614  $prevchar = $chardata[($i-1)];
16615  } else {
16616  $prevchar = false;
16617  }
16618  if (($i+1) < $numchars) {
16619  $nextchar = $chardata[($i+1)];
16620  } else {
16621  $nextchar = false;
16622  }
16623  if ($this->unicode->uni_type[$thischar['char']] == 'AL') {
16624  $x = $thischar['x'];
16625  if ($x > 0) {
16626  $prevchar = $charAL[($x-1)];
16627  } else {
16628  $prevchar = false;
16629  }
16630  if (($x+1) < $numAL) {
16631  $nextchar = $charAL[($x+1)];
16632  } else {
16633  $nextchar = false;
16634  }
16635  // if laa letter
16636  if (($prevchar !== false) AND ($prevchar['char'] == 1604) AND (in_array($thischar['char'], $alfletter))) {
16637  $arabicarr = $this->unicode->uni_laa_array;
16638  $laaletter = true;
16639  if ($x > 1) {
16640  $prevchar = $charAL[($x-2)];
16641  } else {
16642  $prevchar = false;
16643  }
16644  } else {
16645  $arabicarr = $this->unicode->uni_arabicsubst;
16646  $laaletter = false;
16647  }
16648  if (($prevchar !== false) AND ($nextchar !== false) AND
16649  (($this->unicode->uni_type[$prevchar['char']] == 'AL') OR ($this->unicode->uni_type[$prevchar['char']] == 'NSM')) AND
16650  (($this->unicode->uni_type[$nextchar['char']] == 'AL') OR ($this->unicode->uni_type[$nextchar['char']] == 'NSM')) AND
16651  ($prevchar['type'] == $thischar['type']) AND
16652  ($nextchar['type'] == $thischar['type']) AND
16653  ($nextchar['char'] != 1567)) {
16654  if (in_array($prevchar['char'], $endedletter)) {
16655  if (isset($arabicarr[$thischar['char']][2])) {
16656  // initial
16657  $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
16658  }
16659  } else {
16660  if (isset($arabicarr[$thischar['char']][3])) {
16661  // medial
16662  $chardata2[$i]['char'] = $arabicarr[$thischar['char']][3];
16663  }
16664  }
16665  } elseif (($nextchar !== false) AND
16666  (($this->unicode->uni_type[$nextchar['char']] == 'AL') OR ($this->unicode->uni_type[$nextchar['char']] == 'NSM')) AND
16667  ($nextchar['type'] == $thischar['type']) AND
16668  ($nextchar['char'] != 1567)) {
16669  if (isset($arabicarr[$chardata[$i]['char']][2])) {
16670  // initial
16671  $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
16672  }
16673  } elseif ((($prevchar !== false) AND
16674  (($this->unicode->uni_type[$prevchar['char']] == 'AL') OR ($this->unicode->uni_type[$prevchar['char']] == 'NSM')) AND
16675  ($prevchar['type'] == $thischar['type'])) OR
16676  (($nextchar !== false) AND ($nextchar['char'] == 1567))) {
16677  // final
16678  if (($i > 1) AND ($thischar['char'] == 1607) AND
16679  ($chardata[$i-1]['char'] == 1604) AND
16680  ($chardata[$i-2]['char'] == 1604)) {
16681  //Allah Word
16682  // mark characters to delete with false
16683  $chardata2[$i-2]['char'] = false;
16684  $chardata2[$i-1]['char'] = false;
16685  $chardata2[$i]['char'] = 65010;
16686  } else {
16687  if (($prevchar !== false) AND in_array($prevchar['char'], $endedletter)) {
16688  if (isset($arabicarr[$thischar['char']][0])) {
16689  // isolated
16690  $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
16691  }
16692  } else {
16693  if (isset($arabicarr[$thischar['char']][1])) {
16694  // final
16695  $chardata2[$i]['char'] = $arabicarr[$thischar['char']][1];
16696  }
16697  }
16698  }
16699  } elseif (isset($arabicarr[$thischar['char']][0])) {
16700  // isolated
16701  $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
16702  }
16703  // if laa letter
16704  if ($laaletter) {
16705  // mark characters to delete with false
16706  $chardata2[($charAL[($x-1)]['i'])]['char'] = false;
16707  }
16708  } // end if AL (Arabic Letter)
16709  } // end for each char
16710  /*
16711  * Combining characters that can occur with Arabic Shadda (0651 HEX, 1617 DEC) are replaced.
16712  * Putting the combining mark and shadda in the same glyph allows us to avoid the two marks overlapping each other in an illegible manner.
16713  */
16714  for ($i = 0; $i < ($numchars-1); ++$i) {
16715  if (($chardata2[$i]['char'] == 1617) AND (isset($this->unicode->uni_diacritics[($chardata2[$i+1]['char'])]))) {
16716  // check if the subtitution font is defined on current font
16717  if (isset($this->CurrentFont['cw'][($this->unicode->uni_diacritics[($chardata2[$i+1]['char'])])])) {
16718  $chardata2[$i]['char'] = false;
16719  $chardata2[$i+1]['char'] = $this->unicode->uni_diacritics[($chardata2[$i+1]['char'])];
16720  }
16721  }
16722  }
16723  // remove marked characters
16724  foreach ($chardata2 as $key => $value) {
16725  if ($value['char'] === false) {
16726  unset($chardata2[$key]);
16727  }
16728  }
16729  $chardata = array_values($chardata2);
16730  $numchars = count($chardata);
16731  unset($chardata2);
16732  unset($arabicarr);
16733  unset($laaletter);
16734  unset($charAL);
16735  }
16736 
16737  // L2. From the highest level found in the text to the lowest odd level on each line, including intermediate levels not actually present in the text, reverse any contiguous sequence of characters that are at that level or higher.
16738  for ($j=$maxlevel; $j > 0; $j--) {
16739  $ordarray = Array();
16740  $revarr = Array();
16741  $onlevel = false;
16742  for ($i=0; $i < $numchars; ++$i) {
16743  if ($chardata[$i]['level'] >= $j) {
16744  $onlevel = true;
16745  if (isset($this->unicode->uni_mirror[$chardata[$i]['char']])) {
16746  // L4. A character is depicted by a mirrored glyph if and only if (a) the resolved directionality of that character is R, and (b) the Bidi_Mirrored property value of that character is true.
16747  $chardata[$i]['char'] = $this->unicode->uni_mirror[$chardata[$i]['char']];
16748  }
16749  $revarr[] = $chardata[$i];
16750  } else {
16751  if ($onlevel) {
16752  $revarr = array_reverse($revarr);
16753  $ordarray = array_merge($ordarray, $revarr);
16754  $revarr = Array();
16755  $onlevel = false;
16756  }
16757  $ordarray[] = $chardata[$i];
16758  }
16759  }
16760  if ($onlevel) {
16761  $revarr = array_reverse($revarr);
16762  $ordarray = array_merge($ordarray, $revarr);
16763  }
16764  $chardata = $ordarray;
16765  }
16766 
16767  $ordarray = array();
16768  for ($i=0; $i < $numchars; ++$i) {
16769  $ordarray[] = $chardata[$i]['char'];
16770  // store char values for subsetting
16771  $this->CurrentFont['subsetchars'][$chardata[$i]['char']] = true;
16772  }
16773  // update font subsetchars
16774  $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
16775  return $ordarray;
16776  }
16777 
16778  // END OF BIDIRECTIONAL TEXT SECTION -------------------
16779 
16788  protected function encodeNameObject($name) {
16789  $escname = '';
16790  $length = strlen($name);
16791  for ($i = 0; $i < $length; ++$i) {
16792  $chr = $name[$i];
16793  if (preg_match('/[0-9a-zA-Z]/', $chr) == 1) {
16794  $escname .= $chr;
16795  } else {
16796  $escname .= sprintf('#%02X', ord($chr));
16797  }
16798  }
16799  return $escname;
16800  }
16801 
16814  public function setDestination($name, $y=-1, $page='', $x=-1) {
16815  // remove unsupported characters
16816  $name = $this->encodeNameObject($name);
16817  if ($this->empty_string($name)) {
16818  return false;
16819  }
16820  if ($y == -1) {
16821  $y = $this->GetY();
16822  } elseif ($y < 0) {
16823  $y = 0;
16824  } elseif ($y > $this->h) {
16825  $y = $this->h;
16826  }
16827  if ($x == -1) {
16828  $x = $this->GetX();
16829  } elseif ($x < 0) {
16830  $x = 0;
16831  } elseif ($x > $this->w) {
16832  $x = $this->w;
16833  }
16834  if (empty($page)) {
16835  $page = $this->PageNo();
16836  if (empty($page)) {
16837  return;
16838  }
16839  }
16840  $this->dests[$name] = array('x' => $x, 'y' => $y, 'p' => $page);
16841  return $name;
16842  }
16843 
16851  public function getDestination() {
16852  return $this->dests;
16853  }
16854 
16861  protected function _putdests() {
16862  if (empty($this->dests)) {
16863  return;
16864  }
16865  $this->n_dests = $this->_newobj();
16866  $out = ' <<';
16867  foreach($this->dests as $name => $o) {
16868  $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)));
16869  }
16870  $out .= ' >>';
16871  $out .= "\n".'endobj';
16872  $this->_out($out);
16873  }
16874 
16885  public function setBookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0)) {
16886  $this->Bookmark($txt, $level, $y, $page, $style, $color);
16887  }
16888 
16902  public function Bookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1) {
16903  if ($level < 0) {
16904  $level = 0;
16905  }
16906  if (isset($this->outlines[0])) {
16907  $lastoutline = end($this->outlines);
16908  $maxlevel = $lastoutline['l'] + 1;
16909  } else {
16910  $maxlevel = 0;
16911  }
16912  if ($level > $maxlevel) {
16913  $level = $maxlevel;
16914  }
16915  if ($y == -1) {
16916  $y = $this->GetY();
16917  } elseif ($y < 0) {
16918  $y = 0;
16919  } elseif ($y > $this->h) {
16920  $y = $this->h;
16921  }
16922  if ($x == -1) {
16923  $x = $this->GetX();
16924  } elseif ($x < 0) {
16925  $x = 0;
16926  } elseif ($x > $this->w) {
16927  $x = $this->w;
16928  }
16929  if (empty($page)) {
16930  $page = $this->PageNo();
16931  if (empty($page)) {
16932  return;
16933  }
16934  }
16935  $this->outlines[] = array('t' => $txt, 'l' => $level, 'x' => $x, 'y' => $y, 'p' => $page, 's' => strtoupper($style), 'c' => $color);
16936  }
16937 
16943  protected function sortBookmarks() {
16944  // get sorting columns
16945  $outline_p = array();
16946  $outline_y = array();
16947  foreach ($this->outlines as $key => $row) {
16948  $outline_p[$key] = $row['p'];
16949  $outline_k[$key] = $key;
16950  }
16951  // sort outlines by page and original position
16952  array_multisort($outline_p, SORT_NUMERIC, SORT_ASC, $outline_k, SORT_NUMERIC, SORT_ASC, $this->outlines);
16953  }
16954 
16961  protected function _putbookmarks() {
16962  $nb = count($this->outlines);
16963  if ($nb == 0) {
16964  return;
16965  }
16966  // sort bookmarks
16967  $this->sortBookmarks();
16968  $lru = array();
16969  $level = 0;
16970  foreach ($this->outlines as $i => $o) {
16971  if ($o['l'] > 0) {
16972  $parent = $lru[($o['l'] - 1)];
16973  //Set parent and last pointers
16974  $this->outlines[$i]['parent'] = $parent;
16975  $this->outlines[$parent]['last'] = $i;
16976  if ($o['l'] > $level) {
16977  //Level increasing: set first pointer
16978  $this->outlines[$parent]['first'] = $i;
16979  }
16980  } else {
16981  $this->outlines[$i]['parent'] = $nb;
16982  }
16983  if (($o['l'] <= $level) AND ($i > 0)) {
16984  //Set prev and next pointers
16985  $prev = $lru[$o['l']];
16986  $this->outlines[$prev]['next'] = $i;
16987  $this->outlines[$i]['prev'] = $prev;
16988  }
16989  $lru[$o['l']] = $i;
16990  $level = $o['l'];
16991  }
16992  //Outline items
16993  $n = $this->n + 1;
16994  $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';
16995  foreach ($this->outlines as $i => $o) {
16996  $oid = $this->_newobj();
16997  // covert HTML title to string
16998  $title = preg_replace($nltags, "\n", $o['t']);
16999  $title = preg_replace("/[\r]+/si", '', $title);
17000  $title = preg_replace("/[\n]+/si", "\n", $title);
17001  $title = strip_tags($title);
17002  $title = $this->stringTrim($title);
17003  $out = '<</Title '.$this->_textstring($title, $oid);
17004  $out .= ' /Parent '.($n + $o['parent']).' 0 R';
17005  if (isset($o['prev'])) {
17006  $out .= ' /Prev '.($n + $o['prev']).' 0 R';
17007  }
17008  if (isset($o['next'])) {
17009  $out .= ' /Next '.($n + $o['next']).' 0 R';
17010  }
17011  if (isset($o['first'])) {
17012  $out .= ' /First '.($n + $o['first']).' 0 R';
17013  }
17014  if (isset($o['last'])) {
17015  $out .= ' /Last '.($n + $o['last']).' 0 R';
17016  }
17017  if (isset($this->page_obj_id[($o['p'])])) {
17018  $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)));
17019  }
17020  // set font style
17021  $style = 0;
17022  if (!empty($o['s'])) {
17023  // bold
17024  if (strpos($o['s'], 'B') !== false) {
17025  $style |= 2;
17026  }
17027  // oblique
17028  if (strpos($o['s'], 'I') !== false) {
17029  $style |= 1;
17030  }
17031  }
17032  $out .= sprintf(' /F %d', $style);
17033  // set bookmark color
17034  if (isset($o['c']) AND is_array($o['c']) AND (count($o['c']) == 3)) {
17035  $color = array_values($o['c']);
17036  $out .= sprintf(' /C [%F %F %F]', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255));
17037  } else {
17038  // black
17039  $out .= ' /C [0.0 0.0 0.0]';
17040  }
17041  $out .= ' /Count 0'; // normally closed item
17042  $out .= ' >>';
17043  $out .= "\n".'endobj';
17044  $this->_out($out);
17045  }
17046  //Outline root
17047  $this->OutlineRoot = $this->_newobj();
17048  $this->_out('<< /Type /Outlines /First '.$n.' 0 R /Last '.($n + $lru[0]).' 0 R >>'."\n".'endobj');
17049  }
17050 
17051  // --- JAVASCRIPT ------------------------------------------------------
17052 
17060  public function IncludeJS($script) {
17061  $this->javascript .= $script;
17062  }
17063 
17073  public function addJavascriptObject($script, $onload=false) {
17074  if ($this->pdfa_mode) {
17075  // javascript is not allowed in PDF/A mode
17076  return false;
17077  }
17078  ++$this->n;
17079  $this->js_objects[$this->n] = array('n' => $this->n, 'js' => $script, 'onload' => $onload);
17080  return $this->n;
17081  }
17082 
17089  protected function _putjavascript() {
17090  if ($this->pdfa_mode OR (empty($this->javascript) AND empty($this->js_objects))) {
17091  return;
17092  }
17093  if (strpos($this->javascript, 'this.addField') > 0) {
17094  if (!$this->ur['enabled']) {
17095  //$this->setUserRights();
17096  }
17097  // the following two lines are used to avoid form fields duplication after saving
17098  // The addField method only works when releasing user rights (UR3)
17099  $jsa = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%F,%F,%F,%F]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1);
17100  $jsb = "getField('tcpdfdocsaved').value='saved';";
17101  $this->javascript = $jsa."\n".$this->javascript."\n".$jsb;
17102  }
17103  $this->n_js = $this->_newobj();
17104  $out = ' << /Names [';
17105  if (!empty($this->javascript)) {
17106  $out .= ' (EmbeddedJS) '.($this->n + 1).' 0 R';
17107  }
17108  if (!empty($this->js_objects)) {
17109  foreach ($this->js_objects as $key => $val) {
17110  if ($val['onload']) {
17111  $out .= ' (JS'.$key.') '.$key.' 0 R';
17112  }
17113  }
17114  }
17115  $out .= ' ] >>';
17116  $out .= "\n".'endobj';
17117  $this->_out($out);
17118  // default Javascript object
17119  if (!empty($this->javascript)) {
17120  $obj_id = $this->_newobj();
17121  $out = '<< /S /JavaScript';
17122  $out .= ' /JS '.$this->_textstring($this->javascript, $obj_id);
17123  $out .= ' >>';
17124  $out .= "\n".'endobj';
17125  $this->_out($out);
17126  }
17127  // additional Javascript objects
17128  if (!empty($this->js_objects)) {
17129  foreach ($this->js_objects as $key => $val) {
17130  $out = $this->_getobj($key)."\n".' << /S /JavaScript /JS '.$this->_textstring($val['js'], $key).' >>'."\n".'endobj';
17131  $this->_out($out);
17132  }
17133  }
17134  }
17135 
17143  protected function _JScolor($color) {
17144  static $aColors = array('transparent', 'black', 'white', 'red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'dkGray', 'gray', 'ltGray');
17145  if (substr($color,0,1) == '#') {
17146  return sprintf("['RGB',%F,%F,%F]", hexdec(substr($color,1,2))/255, hexdec(substr($color,3,2))/255, hexdec(substr($color,5,2))/255);
17147  }
17148  if (!in_array($color,$aColors)) {
17149  $this->Error('Invalid color: '.$color);
17150  }
17151  return 'color.'.$color;
17152  }
17153 
17167  protected function _addfield($type, $name, $x, $y, $w, $h, $prop) {
17168  if ($this->rtl) {
17169  $x = $x - $w;
17170  }
17171  // the followind avoid fields duplication after saving the document
17172  $this->javascript .= "if (getField('tcpdfdocsaved').value != 'saved') {";
17173  $k = $this->k;
17174  $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";
17175  $this->javascript .= 'f'.$name.'.textSize='.$this->FontSizePt.";\n";
17176  while (list($key, $val) = each($prop)) {
17177  if (strcmp(substr($key, -5), 'Color') == 0) {
17178  $val = $this->_JScolor($val);
17179  } else {
17180  $val = "'".$val."'";
17181  }
17182  $this->javascript .= 'f'.$name.'.'.$key.'='.$val.";\n";
17183  }
17184  if ($this->rtl) {
17185  $this->x -= $w;
17186  } else {
17187  $this->x += $w;
17188  }
17189  $this->javascript .= '}';
17190  }
17191 
17192  // --- FORM FIELDS -----------------------------------------------------
17193 
17202  protected function getAnnotOptFromJSProp($prop) {
17203  if (isset($prop['aopt']) AND is_array($prop['aopt'])) {
17204  // the annotation options area lready defined
17205  return $prop['aopt'];
17206  }
17207  $opt = array(); // value to be returned
17208  // alignment: Controls how the text is laid out within the text field.
17209  if (isset($prop['alignment'])) {
17210  switch ($prop['alignment']) {
17211  case 'left': {
17212  $opt['q'] = 0;
17213  break;
17214  }
17215  case 'center': {
17216  $opt['q'] = 1;
17217  break;
17218  }
17219  case 'right': {
17220  $opt['q'] = 2;
17221  break;
17222  }
17223  default: {
17224  $opt['q'] = ($this->rtl)?2:0;
17225  break;
17226  }
17227  }
17228  }
17229  // lineWidth: Specifies the thickness of the border when stroking the perimeter of a field's rectangle.
17230  if (isset($prop['lineWidth'])) {
17231  $linewidth = intval($prop['lineWidth']);
17232  } else {
17233  $linewidth = 1;
17234  }
17235  // borderStyle: The border style for a field.
17236  if (isset($prop['borderStyle'])) {
17237  switch ($prop['borderStyle']) {
17238  case 'border.d':
17239  case 'dashed': {
17240  $opt['border'] = array(0, 0, $linewidth, array(3, 2));
17241  $opt['bs'] = array('w'=>$linewidth, 's'=>'D', 'd'=>array(3, 2));
17242  break;
17243  }
17244  case 'border.b':
17245  case 'beveled': {
17246  $opt['border'] = array(0, 0, $linewidth);
17247  $opt['bs'] = array('w'=>$linewidth, 's'=>'B');
17248  break;
17249  }
17250  case 'border.i':
17251  case 'inset': {
17252  $opt['border'] = array(0, 0, $linewidth);
17253  $opt['bs'] = array('w'=>$linewidth, 's'=>'I');
17254  break;
17255  }
17256  case 'border.u':
17257  case 'underline': {
17258  $opt['border'] = array(0, 0, $linewidth);
17259  $opt['bs'] = array('w'=>$linewidth, 's'=>'U');
17260  break;
17261  }
17262  case 'border.s':
17263  case 'solid': {
17264  $opt['border'] = array(0, 0, $linewidth);
17265  $opt['bs'] = array('w'=>$linewidth, 's'=>'S');
17266  break;
17267  }
17268  default: {
17269  break;
17270  }
17271  }
17272  }
17273  if (isset($prop['border']) AND is_array($prop['border'])) {
17274  $opt['border'] = $prop['border'];
17275  }
17276  if (!isset($opt['mk'])) {
17277  $opt['mk'] = array();
17278  }
17279  if (!isset($opt['mk']['if'])) {
17280  $opt['mk']['if'] = array();
17281  }
17282  $opt['mk']['if']['a'] = array(0.5, 0.5);
17283  // buttonAlignX: Controls how space is distributed from the left of the button face with respect to the icon.
17284  if (isset($prop['buttonAlignX'])) {
17285  $opt['mk']['if']['a'][0] = $prop['buttonAlignX'];
17286  }
17287  // buttonAlignY: Controls how unused space is distributed from the bottom of the button face with respect to the icon.
17288  if (isset($prop['buttonAlignY'])) {
17289  $opt['mk']['if']['a'][1] = $prop['buttonAlignY'];
17290  }
17291  // buttonFitBounds: If true, the extent to which the icon may be scaled is set to the bounds of the button field.
17292  if (isset($prop['buttonFitBounds']) AND ($prop['buttonFitBounds'] == 'true')) {
17293  $opt['mk']['if']['fb'] = true;
17294  }
17295  // buttonScaleHow: Controls how the icon is scaled (if necessary) to fit inside the button face.
17296  if (isset($prop['buttonScaleHow'])) {
17297  switch ($prop['buttonScaleHow']) {
17298  case 'scaleHow.proportional': {
17299  $opt['mk']['if']['s'] = 'P';
17300  break;
17301  }
17302  case 'scaleHow.anamorphic': {
17303  $opt['mk']['if']['s'] = 'A';
17304  break;
17305  }
17306  }
17307  }
17308  // buttonScaleWhen: Controls when an icon is scaled to fit inside the button face.
17309  if (isset($prop['buttonScaleWhen'])) {
17310  switch ($prop['buttonScaleWhen']) {
17311  case 'scaleWhen.always': {
17312  $opt['mk']['if']['sw'] = 'A';
17313  break;
17314  }
17315  case 'scaleWhen.never': {
17316  $opt['mk']['if']['sw'] = 'N';
17317  break;
17318  }
17319  case 'scaleWhen.tooBig': {
17320  $opt['mk']['if']['sw'] = 'B';
17321  break;
17322  }
17323  case 'scaleWhen.tooSmall': {
17324  $opt['mk']['if']['sw'] = 'S';
17325  break;
17326  }
17327  }
17328  }
17329  // buttonPosition: Controls how the text and the icon of the button are positioned with respect to each other within the button face.
17330  if (isset($prop['buttonPosition'])) {
17331  switch ($prop['buttonPosition']) {
17332  case 0:
17333  case 'position.textOnly': {
17334  $opt['mk']['tp'] = 0;
17335  break;
17336  }
17337  case 1:
17338  case 'position.iconOnly': {
17339  $opt['mk']['tp'] = 1;
17340  break;
17341  }
17342  case 2:
17343  case 'position.iconTextV': {
17344  $opt['mk']['tp'] = 2;
17345  break;
17346  }
17347  case 3:
17348  case 'position.textIconV': {
17349  $opt['mk']['tp'] = 3;
17350  break;
17351  }
17352  case 4:
17353  case 'position.iconTextH': {
17354  $opt['mk']['tp'] = 4;
17355  break;
17356  }
17357  case 5:
17358  case 'position.textIconH': {
17359  $opt['mk']['tp'] = 5;
17360  break;
17361  }
17362  case 6:
17363  case 'position.overlay': {
17364  $opt['mk']['tp'] = 6;
17365  break;
17366  }
17367  }
17368  }
17369  // fillColor: Specifies the background color for a field.
17370  if (isset($prop['fillColor'])) {
17371  if (is_array($prop['fillColor'])) {
17372  $opt['mk']['bg'] = $prop['fillColor'];
17373  } else {
17374  $opt['mk']['bg'] = $this->convertHTMLColorToDec($prop['fillColor']);
17375  }
17376  }
17377  // strokeColor: Specifies the stroke color for a field that is used to stroke the rectangle of the field with a line as large as the line width.
17378  if (isset($prop['strokeColor'])) {
17379  if (is_array($prop['strokeColor'])) {
17380  $opt['mk']['bc'] = $prop['strokeColor'];
17381  } else {
17382  $opt['mk']['bc'] = $this->convertHTMLColorToDec($prop['strokeColor']);
17383  }
17384  }
17385  // rotation: The rotation of a widget in counterclockwise increments.
17386  if (isset($prop['rotation'])) {
17387  $opt['mk']['r'] = $prop['rotation'];
17388  }
17389  // charLimit: Limits the number of characters that a user can type into a text field.
17390  if (isset($prop['charLimit'])) {
17391  $opt['maxlen'] = intval($prop['charLimit']);
17392  }
17393  if (!isset($ff)) {
17394  $ff = 0; // default value
17395  }
17396  // readonly: The read-only characteristic of a field. If a field is read-only, the user can see the field but cannot change it.
17397  if (isset($prop['readonly']) AND ($prop['readonly'] == 'true')) {
17398  $ff += 1 << 0;
17399  }
17400  // required: Specifies whether a field requires a value.
17401  if (isset($prop['required']) AND ($prop['required'] == 'true')) {
17402  $ff += 1 << 1;
17403  }
17404  // multiline: Controls how text is wrapped within the field.
17405  if (isset($prop['multiline']) AND ($prop['multiline'] == 'true')) {
17406  $ff += 1 << 12;
17407  }
17408  // password: Specifies whether the field should display asterisks when data is entered in the field.
17409  if (isset($prop['password']) AND ($prop['password'] == 'true')) {
17410  $ff += 1 << 13;
17411  }
17412  // NoToggleToOff: If set, exactly one radio button shall be selected at all times; selecting the currently selected button has no effect.
17413  if (isset($prop['NoToggleToOff']) AND ($prop['NoToggleToOff'] == 'true')) {
17414  $ff += 1 << 14;
17415  }
17416  // Radio: If set, the field is a set of radio buttons.
17417  if (isset($prop['Radio']) AND ($prop['Radio'] == 'true')) {
17418  $ff += 1 << 15;
17419  }
17420  // Pushbutton: If set, the field is a pushbutton that does not retain a permanent value.
17421  if (isset($prop['Pushbutton']) AND ($prop['Pushbutton'] == 'true')) {
17422  $ff += 1 << 16;
17423  }
17424  // Combo: If set, the field is a combo box; if clear, the field is a list box.
17425  if (isset($prop['Combo']) AND ($prop['Combo'] == 'true')) {
17426  $ff += 1 << 17;
17427  }
17428  // editable: Controls whether a combo box is editable.
17429  if (isset($prop['editable']) AND ($prop['editable'] == 'true')) {
17430  $ff += 1 << 18;
17431  }
17432  // Sort: If set, the field's option items shall be sorted alphabetically.
17433  if (isset($prop['Sort']) AND ($prop['Sort'] == 'true')) {
17434  $ff += 1 << 19;
17435  }
17436  // fileSelect: If true, sets the file-select flag in the Options tab of the text field (Field is Used for File Selection).
17437  if (isset($prop['fileSelect']) AND ($prop['fileSelect'] == 'true')) {
17438  $ff += 1 << 20;
17439  }
17440  // multipleSelection: If true, indicates that a list box allows a multiple selection of items.
17441  if (isset($prop['multipleSelection']) AND ($prop['multipleSelection'] == 'true')) {
17442  $ff += 1 << 21;
17443  }
17444  // doNotSpellCheck: If true, spell checking is not performed on this editable text field.
17445  if (isset($prop['doNotSpellCheck']) AND ($prop['doNotSpellCheck'] == 'true')) {
17446  $ff += 1 << 22;
17447  }
17448  // doNotScroll: If true, the text field does not scroll and the user, therefore, is limited by the rectangular region designed for the field.
17449  if (isset($prop['doNotScroll']) AND ($prop['doNotScroll'] == 'true')) {
17450  $ff += 1 << 23;
17451  }
17452  // comb: If set to true, the field background is drawn as series of boxes (one for each character in the value of the field) and each character of the content is drawn within those boxes. The number of boxes drawn is determined from the charLimit property. It applies only to text fields. The setter will also raise if any of the following field properties are also set multiline, password, and fileSelect. A side-effect of setting this property is that the doNotScroll property is also set.
17453  if (isset($prop['comb']) AND ($prop['comb'] == 'true')) {
17454  $ff += 1 << 24;
17455  }
17456  // radiosInUnison: If false, even if a group of radio buttons have the same name and export value, they behave in a mutually exclusive fashion, like HTML radio buttons.
17457  if (isset($prop['radiosInUnison']) AND ($prop['radiosInUnison'] == 'true')) {
17458  $ff += 1 << 25;
17459  }
17460  // richText: If true, the field allows rich text formatting.
17461  if (isset($prop['richText']) AND ($prop['richText'] == 'true')) {
17462  $ff += 1 << 25;
17463  }
17464  // commitOnSelChange: Controls whether a field value is committed after a selection change.
17465  if (isset($prop['commitOnSelChange']) AND ($prop['commitOnSelChange'] == 'true')) {
17466  $ff += 1 << 26;
17467  }
17468  $opt['ff'] = $ff;
17469  // defaultValue: The default value of a field - that is, the value that the field is set to when the form is reset.
17470  if (isset($prop['defaultValue'])) {
17471  $opt['dv'] = $prop['defaultValue'];
17472  }
17473  $f = 4; // default value for annotation flags
17474  // readonly: The read-only characteristic of a field. If a field is read-only, the user can see the field but cannot change it.
17475  if (isset($prop['readonly']) AND ($prop['readonly'] == 'true')) {
17476  $f += 1 << 6;
17477  }
17478  // display: Controls whether the field is hidden or visible on screen and in print.
17479  if (isset($prop['display'])) {
17480  if ($prop['display'] == 'display.visible') {
17481  //
17482  } elseif ($prop['display'] == 'display.hidden') {
17483  $f += 1 << 1;
17484  } elseif ($prop['display'] == 'display.noPrint') {
17485  $f -= 1 << 2;
17486  } elseif ($prop['display'] == 'display.noView') {
17487  $f += 1 << 5;
17488  }
17489  }
17490  $opt['f'] = $f;
17491  // currentValueIndices: Reads and writes single or multiple values of a list box or combo box.
17492  if (isset($prop['currentValueIndices']) AND is_array($prop['currentValueIndices'])) {
17493  $opt['i'] = $prop['currentValueIndices'];
17494  }
17495  // value: The value of the field data that the user has entered.
17496  if (isset($prop['value'])) {
17497  if (is_array($prop['value'])) {
17498  $opt['opt'] = array();
17499  foreach ($prop['value'] AS $key => $optval) {
17500  // exportValues: An array of strings representing the export values for the field.
17501  if (isset($prop['exportValues'][$key])) {
17502  $opt['opt'][$key] = array($prop['exportValues'][$key], $prop['value'][$key]);
17503  } else {
17504  $opt['opt'][$key] = $prop['value'][$key];
17505  }
17506  }
17507  } else {
17508  $opt['v'] = $prop['value'];
17509  }
17510  }
17511  // richValue: This property specifies the text contents and formatting of a rich text field.
17512  if (isset($prop['richValue'])) {
17513  $opt['rv'] = $prop['richValue'];
17514  }
17515  // submitName: If nonempty, used during form submission instead of name. Only applicable if submitting in HTML format (that is, URL-encoded).
17516  if (isset($prop['submitName'])) {
17517  $opt['tm'] = $prop['submitName'];
17518  }
17519  // name: Fully qualified field name.
17520  if (isset($prop['name'])) {
17521  $opt['t'] = $prop['name'];
17522  }
17523  // userName: The user name (short description string) of the field.
17524  if (isset($prop['userName'])) {
17525  $opt['tu'] = $prop['userName'];
17526  }
17527  // highlight: Defines how a button reacts when a user clicks it.
17528  if (isset($prop['highlight'])) {
17529  switch ($prop['highlight']) {
17530  case 'none':
17531  case 'highlight.n': {
17532  $opt['h'] = 'N';
17533  break;
17534  }
17535  case 'invert':
17536  case 'highlight.i': {
17537  $opt['h'] = 'i';
17538  break;
17539  }
17540  case 'push':
17541  case 'highlight.p': {
17542  $opt['h'] = 'P';
17543  break;
17544  }
17545  case 'outline':
17546  case 'highlight.o': {
17547  $opt['h'] = 'O';
17548  break;
17549  }
17550  }
17551  }
17552  // Unsupported options:
17553  // - calcOrderIndex: Changes the calculation order of fields in the document.
17554  // - delay: Delays the redrawing of a field's appearance.
17555  // - defaultStyle: This property defines the default style attributes for the form field.
17556  // - style: Allows the user to set the glyph style of a check box or radio button.
17557  // - textColor, textFont, textSize
17558  return $opt;
17559  }
17560 
17568  public function setFormDefaultProp($prop=array()) {
17569  $this->default_form_prop = $prop;
17570  }
17571 
17579  public function getFormDefaultProp() {
17580  return $this->default_form_prop;
17581  }
17582 
17597  public function TextField($name, $w, $h, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
17598  if ($x === '') {
17599  $x = $this->x;
17600  }
17601  if ($y === '') {
17602  $y = $this->y;
17603  }
17604  // check page for no-write regions and adapt page margins if necessary
17605  list($x, $y) = $this->checkPageRegions($h, $x, $y);
17606  if ($js) {
17607  $this->_addfield('text', $name, $x, $y, $w, $h, $prop);
17608  return;
17609  }
17610  // get default style
17611  $prop = array_merge($this->getFormDefaultProp(), $prop);
17612  // get annotation data
17613  $popt = $this->getAnnotOptFromJSProp($prop);
17614  // set default appearance stream
17615  $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
17616  $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
17617  $popt['da'] = $fontstyle;
17618  // build appearance stream
17619  $popt['ap'] = array();
17620  $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
17621  $text = '';
17622  if (isset($prop['value']) AND !empty($prop['value'])) {
17623  $text = $prop['value'];
17624  } elseif (isset($opt['v']) AND !empty($opt['v'])) {
17625  $text = $opt['v'];
17626  }
17627  $tmpid = $this->startTemplate($w, $h, false);
17628  $align = '';
17629  if (isset($popt['q'])) {
17630  switch ($popt['q']) {
17631  case 0: {
17632  $align = 'L';
17633  break;
17634  }
17635  case 1: {
17636  $align = 'C';
17637  break;
17638  }
17639  case 2: {
17640  $align = 'R';
17641  break;
17642  }
17643  default: {
17644  $align = '';
17645  break;
17646  }
17647  }
17648  }
17649  $this->MultiCell($w, $h, $text, 0, $align, false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
17650  $this->endTemplate();
17651  --$this->n;
17652  $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
17653  unset($this->xobjects[$tmpid]);
17654  $popt['ap']['n'] .= 'Q EMC';
17655  // merge options
17656  $opt = array_merge($popt, $opt);
17657  // remove some conflicting options
17658  unset($opt['bs']);
17659  // set remaining annotation data
17660  $opt['Subtype'] = 'Widget';
17661  $opt['ft'] = 'Tx';
17662  $opt['t'] = $name;
17663  // Additional annotation's parameters (check _putannotsobj() method):
17664  //$opt['f']
17665  //$opt['as']
17666  //$opt['bs']
17667  //$opt['be']
17668  //$opt['c']
17669  //$opt['border']
17670  //$opt['h']
17671  //$opt['mk'];
17672  //$opt['mk']['r']
17673  //$opt['mk']['bc'];
17674  //$opt['mk']['bg'];
17675  unset($opt['mk']['ca']);
17676  unset($opt['mk']['rc']);
17677  unset($opt['mk']['ac']);
17678  unset($opt['mk']['i']);
17679  unset($opt['mk']['ri']);
17680  unset($opt['mk']['ix']);
17681  unset($opt['mk']['if']);
17682  //$opt['mk']['if']['sw'];
17683  //$opt['mk']['if']['s'];
17684  //$opt['mk']['if']['a'];
17685  //$opt['mk']['if']['fb'];
17686  unset($opt['mk']['tp']);
17687  //$opt['tu']
17688  //$opt['tm']
17689  //$opt['ff']
17690  //$opt['v']
17691  //$opt['dv']
17692  //$opt['a']
17693  //$opt['aa']
17694  //$opt['q']
17695  $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
17696  if ($this->rtl) {
17697  $this->x -= $w;
17698  } else {
17699  $this->x += $w;
17700  }
17701  }
17702 
17718  public function RadioButton($name, $w, $prop=array(), $opt=array(), $onvalue='On', $checked=false, $x='', $y='', $js=false) {
17719  if ($x === '') {
17720  $x = $this->x;
17721  }
17722  if ($y === '') {
17723  $y = $this->y;
17724  }
17725  // check page for no-write regions and adapt page margins if necessary
17726  list($x, $y) = $this->checkPageRegions($w, $x, $y);
17727  if ($js) {
17728  $this->_addfield('radiobutton', $name, $x, $y, $w, $w, $prop);
17729  return;
17730  }
17731  if ($this->empty_string($onvalue)) {
17732  $onvalue = 'On';
17733  }
17734  if ($checked) {
17735  $defval = $onvalue;
17736  } else {
17737  $defval = 'Off';
17738  }
17739  // set font
17740  $font = 'zapfdingbats';
17741  if ($this->pdfa_mode) {
17742  // all fonts must be embedded
17743  $font = 'pdfa'.$font;
17744  }
17745  $this->AddFont($font);
17746  $tmpfont = $this->getFontBuffer($font);
17747  // set data for parent group
17748  if (!isset($this->radiobutton_groups[$this->page])) {
17749  $this->radiobutton_groups[$this->page] = array();
17750  }
17751  if (!isset($this->radiobutton_groups[$this->page][$name])) {
17752  $this->radiobutton_groups[$this->page][$name] = array();
17753  ++$this->n;
17754  $this->radiobutton_groups[$this->page][$name]['n'] = $this->n;
17755  $this->radio_groups[] = $this->n;
17756  }
17757  $kid = ($this->n + 1);
17758  // save object ID to be added on Kids entry on parent object
17759  $this->radiobutton_groups[$this->page][$name][] = array('kid' => $kid, 'def' => $defval);
17760  // get default style
17761  $prop = array_merge($this->getFormDefaultProp(), $prop);
17762  $prop['NoToggleToOff'] = 'true';
17763  $prop['Radio'] = 'true';
17764  $prop['borderStyle'] = 'inset';
17765  // get annotation data
17766  $popt = $this->getAnnotOptFromJSProp($prop);
17767  // set additional default options
17768  $this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i'];
17769  $fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor);
17770  $popt['da'] = $fontstyle;
17771  // build appearance stream
17772  $popt['ap'] = array();
17773  $popt['ap']['n'] = array();
17774  $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k);
17775  $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, 0, $fy);
17776  $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, 0, $fy);
17777  if (!isset($popt['mk'])) {
17778  $popt['mk'] = array();
17779  }
17780  $popt['mk']['ca'] = '(l)';
17781  // merge options
17782  $opt = array_merge($popt, $opt);
17783  // set remaining annotation data
17784  $opt['Subtype'] = 'Widget';
17785  $opt['ft'] = 'Btn';
17786  if ($checked) {
17787  $opt['v'] = array('/'.$onvalue);
17788  $opt['as'] = $onvalue;
17789  } else {
17790  $opt['as'] = 'Off';
17791  }
17792  // store readonly flag
17793  if (!isset($this->radiobutton_groups[$this->page][$name]['#readonly#'])) {
17794  $this->radiobutton_groups[$this->page][$name]['#readonly#'] = false;
17795  }
17796  $this->radiobutton_groups[$this->page][$name]['#readonly#'] |= ($opt['f'] & 64);
17797  $this->Annotation($x, $y, $w, $w, $name, $opt, 0);
17798  if ($this->rtl) {
17799  $this->x -= $w;
17800  } else {
17801  $this->x += $w;
17802  }
17803  }
17804 
17820  public function ListBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
17821  if ($x === '') {
17822  $x = $this->x;
17823  }
17824  if ($y === '') {
17825  $y = $this->y;
17826  }
17827  // check page for no-write regions and adapt page margins if necessary
17828  list($x, $y) = $this->checkPageRegions($h, $x, $y);
17829  if ($js) {
17830  $this->_addfield('listbox', $name, $x, $y, $w, $h, $prop);
17831  $s = '';
17832  foreach ($values as $value) {
17833  if (is_array($value)) {
17834  $s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']';
17835  } else {
17836  $s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']';
17837  }
17838  }
17839  $this->javascript .= 'f'.$name.'.setItems('.substr($s, 1).');'."\n";
17840  return;
17841  }
17842  // get default style
17843  $prop = array_merge($this->getFormDefaultProp(), $prop);
17844  // get annotation data
17845  $popt = $this->getAnnotOptFromJSProp($prop);
17846  // set additional default values
17847  $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
17848  $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
17849  $popt['da'] = $fontstyle;
17850  // build appearance stream
17851  $popt['ap'] = array();
17852  $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
17853  $text = '';
17854  foreach($values as $item) {
17855  if (is_array($item)) {
17856  $text .= $item[1]."\n";
17857  } else {
17858  $text .= $item."\n";
17859  }
17860  }
17861  $tmpid = $this->startTemplate($w, $h, false);
17862  $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
17863  $this->endTemplate();
17864  --$this->n;
17865  $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
17866  unset($this->xobjects[$tmpid]);
17867  $popt['ap']['n'] .= 'Q EMC';
17868  // merge options
17869  $opt = array_merge($popt, $opt);
17870  // set remaining annotation data
17871  $opt['Subtype'] = 'Widget';
17872  $opt['ft'] = 'Ch';
17873  $opt['t'] = $name;
17874  $opt['opt'] = $values;
17875  unset($opt['mk']['ca']);
17876  unset($opt['mk']['rc']);
17877  unset($opt['mk']['ac']);
17878  unset($opt['mk']['i']);
17879  unset($opt['mk']['ri']);
17880  unset($opt['mk']['ix']);
17881  unset($opt['mk']['if']);
17882  unset($opt['mk']['tp']);
17883  $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
17884  if ($this->rtl) {
17885  $this->x -= $w;
17886  } else {
17887  $this->x += $w;
17888  }
17889  }
17890 
17906  public function ComboBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
17907  if ($x === '') {
17908  $x = $this->x;
17909  }
17910  if ($y === '') {
17911  $y = $this->y;
17912  }
17913  // check page for no-write regions and adapt page margins if necessary
17914  list($x, $y) = $this->checkPageRegions($h, $x, $y);
17915  if ($js) {
17916  $this->_addfield('combobox', $name, $x, $y, $w, $h, $prop);
17917  $s = '';
17918  foreach ($values as $value) {
17919  if (is_array($value)) {
17920  $s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']';
17921  } else {
17922  $s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']';
17923  }
17924  }
17925  $this->javascript .= 'f'.$name.'.setItems('.substr($s, 1).');'."\n";
17926  return;
17927  }
17928  // get default style
17929  $prop = array_merge($this->getFormDefaultProp(), $prop);
17930  $prop['Combo'] = true;
17931  // get annotation data
17932  $popt = $this->getAnnotOptFromJSProp($prop);
17933  // set additional default options
17934  $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
17935  $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
17936  $popt['da'] = $fontstyle;
17937  // build appearance stream
17938  $popt['ap'] = array();
17939  $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
17940  $text = '';
17941  foreach($values as $item) {
17942  if (is_array($item)) {
17943  $text .= $item[1]."\n";
17944  } else {
17945  $text .= $item."\n";
17946  }
17947  }
17948  $tmpid = $this->startTemplate($w, $h, false);
17949  $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
17950  $this->endTemplate();
17951  --$this->n;
17952  $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
17953  unset($this->xobjects[$tmpid]);
17954  $popt['ap']['n'] .= 'Q EMC';
17955  // merge options
17956  $opt = array_merge($popt, $opt);
17957  // set remaining annotation data
17958  $opt['Subtype'] = 'Widget';
17959  $opt['ft'] = 'Ch';
17960  $opt['t'] = $name;
17961  $opt['opt'] = $values;
17962  unset($opt['mk']['ca']);
17963  unset($opt['mk']['rc']);
17964  unset($opt['mk']['ac']);
17965  unset($opt['mk']['i']);
17966  unset($opt['mk']['ri']);
17967  unset($opt['mk']['ix']);
17968  unset($opt['mk']['if']);
17969  unset($opt['mk']['tp']);
17970  $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
17971  if ($this->rtl) {
17972  $this->x -= $w;
17973  } else {
17974  $this->x += $w;
17975  }
17976  }
17977 
17993  public function CheckBox($name, $w, $checked=false, $prop=array(), $opt=array(), $onvalue='Yes', $x='', $y='', $js=false) {
17994  if ($x === '') {
17995  $x = $this->x;
17996  }
17997  if ($y === '') {
17998  $y = $this->y;
17999  }
18000  // check page for no-write regions and adapt page margins if necessary
18001  list($x, $y) = $this->checkPageRegions($w, $x, $y);
18002  if ($js) {
18003  $this->_addfield('checkbox', $name, $x, $y, $w, $w, $prop);
18004  return;
18005  }
18006  if (!isset($prop['value'])) {
18007  $prop['value'] = array('Yes');
18008  }
18009  // get default style
18010  $prop = array_merge($this->getFormDefaultProp(), $prop);
18011  $prop['borderStyle'] = 'inset';
18012  // get annotation data
18013  $popt = $this->getAnnotOptFromJSProp($prop);
18014  // set additional default options
18015  $font = 'zapfdingbats';
18016  if ($this->pdfa_mode) {
18017  // all fonts must be embedded
18018  $font = 'pdfa'.$font;
18019  }
18020  $this->AddFont($font);
18021  $tmpfont = $this->getFontBuffer($font);
18022  $this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i'];
18023  $fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor);
18024  $popt['da'] = $fontstyle;
18025  // build appearance stream
18026  $popt['ap'] = array();
18027  $popt['ap']['n'] = array();
18028  $fy = ((($tmpfont['desc']['Ascent'] + $tmpfont['desc']['Descent']) * $this->FontSizePt) / (1000 * $this->k));
18029  $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k);
18030  $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, 0, $fy);
18031  $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, 0, $fy);
18032  // merge options
18033  $opt = array_merge($popt, $opt);
18034  // set remaining annotation data
18035  $opt['Subtype'] = 'Widget';
18036  $opt['ft'] = 'Btn';
18037  $opt['t'] = $name;
18038  if ($this->empty_string($onvalue)) {
18039  $onvalue = 'Yes';
18040  }
18041  $opt['opt'] = array($onvalue);
18042  if ($checked) {
18043  $opt['v'] = array('/Yes');
18044  $opt['as'] = 'Yes';
18045  } else {
18046  $opt['v'] = array('/Off');
18047  $opt['as'] = 'Off';
18048  }
18049  $this->Annotation($x, $y, $w, $w, $name, $opt, 0);
18050  if ($this->rtl) {
18051  $this->x -= $w;
18052  } else {
18053  $this->x += $w;
18054  }
18055  }
18056 
18073  public function Button($name, $w, $h, $caption, $action, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
18074  if ($x === '') {
18075  $x = $this->x;
18076  }
18077  if ($y === '') {
18078  $y = $this->y;
18079  }
18080  // check page for no-write regions and adapt page margins if necessary
18081  list($x, $y) = $this->checkPageRegions($h, $x, $y);
18082  if ($js) {
18083  $this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop);
18084  $this->javascript .= 'f'.$name.".buttonSetCaption('".addslashes($caption)."');\n";
18085  $this->javascript .= 'f'.$name.".setAction('MouseUp','".addslashes($action)."');\n";
18086  $this->javascript .= 'f'.$name.".highlight='push';\n";
18087  $this->javascript .= 'f'.$name.".print=false;\n";
18088  return;
18089  }
18090  // get default style
18091  $prop = array_merge($this->getFormDefaultProp(), $prop);
18092  $prop['Pushbutton'] = 'true';
18093  $prop['highlight'] = 'push';
18094  $prop['display'] = 'display.noPrint';
18095  // get annotation data
18096  $popt = $this->getAnnotOptFromJSProp($prop);
18097  $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
18098  $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
18099  $popt['da'] = $fontstyle;
18100  // build appearance stream
18101  $popt['ap'] = array();
18102  $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
18103  $tmpid = $this->startTemplate($w, $h, false);
18104  $bw = (2 / $this->k); // border width
18105  $border = array(
18106  'L' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)),
18107  'R' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)),
18108  'T' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)),
18109  'B' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)));
18110  $this->SetFillColor(204);
18111  $this->Cell($w, $h, $caption, $border, 0, 'C', true, '', 1, false, 'T', 'M');
18112  $this->endTemplate();
18113  --$this->n;
18114  $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
18115  unset($this->xobjects[$tmpid]);
18116  $popt['ap']['n'] .= 'Q EMC';
18117  // set additional default options
18118  if (!isset($popt['mk'])) {
18119  $popt['mk'] = array();
18120  }
18121  $ann_obj_id = ($this->n + 1);
18122  if (!empty($action) AND !is_array($action)) {
18123  $ann_obj_id = ($this->n + 2);
18124  }
18125  $popt['mk']['ca'] = $this->_textstring($caption, $ann_obj_id);
18126  $popt['mk']['rc'] = $this->_textstring($caption, $ann_obj_id);
18127  $popt['mk']['ac'] = $this->_textstring($caption, $ann_obj_id);
18128  // merge options
18129  $opt = array_merge($popt, $opt);
18130  // set remaining annotation data
18131  $opt['Subtype'] = 'Widget';
18132  $opt['ft'] = 'Btn';
18133  $opt['t'] = $caption;
18134  $opt['v'] = $name;
18135  if (!empty($action)) {
18136  if (is_array($action)) {
18137  // form action options as on section 12.7.5 of PDF32000_2008.
18138  $opt['aa'] = '/D <<';
18139  $bmode = array('SubmitForm', 'ResetForm', 'ImportData');
18140  foreach ($action AS $key => $val) {
18141  if (($key == 'S') AND in_array($val, $bmode)) {
18142  $opt['aa'] .= ' /S /'.$val;
18143  } elseif (($key == 'F') AND (!empty($val))) {
18144  $opt['aa'] .= ' /F '.$this->_datastring($val, $ann_obj_id);
18145  } elseif (($key == 'Fields') AND is_array($val) AND !empty($val)) {
18146  $opt['aa'] .= ' /Fields [';
18147  foreach ($val AS $field) {
18148  $opt['aa'] .= ' '.$this->_textstring($field, $ann_obj_id);
18149  }
18150  $opt['aa'] .= ']';
18151  } elseif (($key == 'Flags')) {
18152  $ff = 0;
18153  if (is_array($val)) {
18154  foreach ($val AS $flag) {
18155  switch ($flag) {
18156  case 'Include/Exclude': {
18157  $ff += 1 << 0;
18158  break;
18159  }
18160  case 'IncludeNoValueFields': {
18161  $ff += 1 << 1;
18162  break;
18163  }
18164  case 'ExportFormat': {
18165  $ff += 1 << 2;
18166  break;
18167  }
18168  case 'GetMethod': {
18169  $ff += 1 << 3;
18170  break;
18171  }
18172  case 'SubmitCoordinates': {
18173  $ff += 1 << 4;
18174  break;
18175  }
18176  case 'XFDF': {
18177  $ff += 1 << 5;
18178  break;
18179  }
18180  case 'IncludeAppendSaves': {
18181  $ff += 1 << 6;
18182  break;
18183  }
18184  case 'IncludeAnnotations': {
18185  $ff += 1 << 7;
18186  break;
18187  }
18188  case 'SubmitPDF': {
18189  $ff += 1 << 8;
18190  break;
18191  }
18192  case 'CanonicalFormat': {
18193  $ff += 1 << 9;
18194  break;
18195  }
18196  case 'ExclNonUserAnnots': {
18197  $ff += 1 << 10;
18198  break;
18199  }
18200  case 'ExclFKey': {
18201  $ff += 1 << 11;
18202  break;
18203  }
18204  case 'EmbedForm': {
18205  $ff += 1 << 13;
18206  break;
18207  }
18208  }
18209  }
18210  } else {
18211  $ff = intval($val);
18212  }
18213  $opt['aa'] .= ' /Flags '.$ff;
18214  }
18215  }
18216  $opt['aa'] .= ' >>';
18217  } else {
18218  // Javascript action or raw action command
18219  $js_obj_id = $this->addJavascriptObject($action);
18220  $opt['aa'] = '/D '.$js_obj_id.' 0 R';
18221  }
18222  }
18223  $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
18224  if ($this->rtl) {
18225  $this->x -= $w;
18226  } else {
18227  $this->x += $w;
18228  }
18229  }
18230 
18231  // --- END FORMS FIELDS ------------------------------------------------
18232 
18240  protected function _putsignature() {
18241  if ((!$this->sign) OR (!isset($this->signature_data['cert_type']))) {
18242  return;
18243  }
18244  $sigobjid = ($this->sig_obj_id + 1);
18245  $out = $this->_getobj($sigobjid)."\n";
18246  $out .= '<< /Type /Sig';
18247  $out .= ' /Filter /Adobe.PPKLite';
18248  $out .= ' /SubFilter /adbe.pkcs7.detached';
18249  $out .= ' '.$this->byterange_string;
18250  $out .= ' /Contents<'.str_repeat('0', $this->signature_max_length).'>';
18251  $out .= ' /Reference ['; // array of signature reference dictionaries
18252  $out .= ' << /Type /SigRef';
18253  if ($this->signature_data['cert_type'] > 0) {
18254  $out .= ' /TransformMethod /DocMDP';
18255  $out .= ' /TransformParams <<';
18256  $out .= ' /Type /TransformParams';
18257  $out .= ' /P '.$this->signature_data['cert_type'];
18258  $out .= ' /V /1.2';
18259  } else {
18260  $out .= ' /TransformMethod /UR3';
18261  $out .= ' /TransformParams <<';
18262  $out .= ' /Type /TransformParams';
18263  $out .= ' /V /2.2';
18264  if (!$this->empty_string($this->ur['document'])) {
18265  $out .= ' /Document['.$this->ur['document'].']';
18266  }
18267  if (!$this->empty_string($this->ur['form'])) {
18268  $out .= ' /Form['.$this->ur['form'].']';
18269  }
18270  if (!$this->empty_string($this->ur['signature'])) {
18271  $out .= ' /Signature['.$this->ur['signature'].']';
18272  }
18273  if (!$this->empty_string($this->ur['annots'])) {
18274  $out .= ' /Annots['.$this->ur['annots'].']';
18275  }
18276  if (!$this->empty_string($this->ur['ef'])) {
18277  $out .= ' /EF['.$this->ur['ef'].']';
18278  }
18279  if (!$this->empty_string($this->ur['formex'])) {
18280  $out .= ' /FormEX['.$this->ur['formex'].']';
18281  }
18282  }
18283  $out .= ' >>'; // close TransformParams
18284  // optional digest data (values must be calculated and replaced later)
18285  //$out .= ' /Data ********** 0 R';
18286  //$out .= ' /DigestMethod/MD5';
18287  //$out .= ' /DigestLocation[********** 34]';
18288  //$out .= ' /DigestValue<********************************>';
18289  $out .= ' >>';
18290  $out .= ' ]'; // end of reference
18291  if (isset($this->signature_data['info']['Name']) AND !$this->empty_string($this->signature_data['info']['Name'])) {
18292  $out .= ' /Name '.$this->_textstring($this->signature_data['info']['Name'], $sigobjid);
18293  }
18294  if (isset($this->signature_data['info']['Location']) AND !$this->empty_string($this->signature_data['info']['Location'])) {
18295  $out .= ' /Location '.$this->_textstring($this->signature_data['info']['Location'], $sigobjid);
18296  }
18297  if (isset($this->signature_data['info']['Reason']) AND !$this->empty_string($this->signature_data['info']['Reason'])) {
18298  $out .= ' /Reason '.$this->_textstring($this->signature_data['info']['Reason'], $sigobjid);
18299  }
18300  if (isset($this->signature_data['info']['ContactInfo']) AND !$this->empty_string($this->signature_data['info']['ContactInfo'])) {
18301  $out .= ' /ContactInfo '.$this->_textstring($this->signature_data['info']['ContactInfo'], $sigobjid);
18302  }
18303  $out .= ' /M '.$this->_datestring($sigobjid, $this->doc_modification_timestamp);
18304  $out .= ' >>';
18305  $out .= "\n".'endobj';
18306  $this->_out($out);
18307  }
18308 
18326  public function setUserRights(
18327  $enable=true,
18328  $document='/FullSave',
18329  $annots='/Create/Delete/Modify/Copy/Import/Export',
18330  $form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate',
18331  $signature='/Modify',
18332  $ef='/Create/Delete/Modify/Import',
18333  $formex='') {
18334  $this->ur['enabled'] = $enable;
18335  $this->ur['document'] = $document;
18336  $this->ur['annots'] = $annots;
18337  $this->ur['form'] = $form;
18338  $this->ur['signature'] = $signature;
18339  $this->ur['ef'] = $ef;
18340  $this->ur['formex'] = $formex;
18341  if (!$this->sign) {
18342  $this->setSignature('', '', '', '', 0, array());
18343  }
18344  }
18345 
18362  public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array()) {
18363  // to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt
18364  // to export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12
18365  // to convert pfx certificate to pem: openssl
18366  // OpenSSL> pkcs12 -in <cert.pfx> -out <cert.crt> -nodes
18367  $this->sign = true;
18368  ++$this->n;
18369  $this->sig_obj_id = $this->n; // signature widget
18370  ++$this->n; // signature object ($this->sig_obj_id + 1)
18371  $this->signature_data = array();
18372  if (strlen($signing_cert) == 0) {
18373  $signing_cert = 'file://'.dirname(__FILE__).'/tcpdf.crt';
18374  $private_key_password = 'tcpdfdemo';
18375  }
18376  if (strlen($private_key) == 0) {
18377  $private_key = $signing_cert;
18378  }
18379  $this->signature_data['signcert'] = $signing_cert;
18380  $this->signature_data['privkey'] = $private_key;
18381  $this->signature_data['password'] = $private_key_password;
18382  $this->signature_data['extracerts'] = $extracerts;
18383  $this->signature_data['cert_type'] = $cert_type;
18384  $this->signature_data['info'] = $info;
18385  }
18386 
18398  public function setSignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1) {
18399  $this->signature_appearance = $this->getSignatureAppearanceArray($x, $y, $w, $h, $page);
18400  }
18401 
18413  public function addEmptySignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1) {
18414  ++$this->n;
18415  $this->empty_signature_appearance[] = array('objid' => $this->n) + $this->getSignatureAppearanceArray($x, $y, $w, $h, $page);
18416  }
18417 
18430  protected function getSignatureAppearanceArray($x=0, $y=0, $w=0, $h=0, $page=-1) {
18431  $sigapp = array();
18432  if (($page < 1) OR ($page > $this->numpages)) {
18433  $sigapp['page'] = $this->page;
18434  } else {
18435  $sigapp['page'] = intval($page);
18436  }
18437  $a = $x * $this->k;
18438  $b = $this->pagedim[($sigapp['page'])]['h'] - (($y + $h) * $this->k);
18439  $c = $w * $this->k;
18440  $d = $h * $this->k;
18441  $sigapp['rect'] = sprintf('%F %F %F %F', $a, $b, ($a + $c), ($b + $d));
18442  return $sigapp;
18443  }
18444 
18452  public function startPageGroup($page = '') {
18453  if (empty($page)) {
18454  $page = $this->page + 1;
18455  }
18456  $this->newpagegroup[$page] = sizeof($this->newpagegroup) + 1;
18457  }
18458 
18466  public function AliasNbPages($s='') {}
18467 
18475  public function AliasNumPage($s='') {}
18476 
18483  public function setStartingPageNumber($num=1) {
18484  $this->starting_page_number = max(0, intval($num));
18485  }
18486 
18494  public function getAliasRightShift() {
18495  // calculate aproximatively the ratio between widths of aliases and replacements.
18496  $ref = '{'.$this->alias_right_shift.'}{'.$this->alias_tot_pages.'}{'.$this->alias_num_page.'}';
18497  $rep = str_repeat(' ', $this->GetNumChars($ref));
18498  $wdiff = max(1, ($this->GetStringWidth($ref) / $this->GetStringWidth($rep)));
18499  $sdiff = sprintf('%F', $wdiff);
18500  $alias = $this->alias_right_shift.$sdiff.'}';
18501  if ($this->isUnicodeFont()) {
18502  $alias = '{'.$alias;
18503  }
18504  return $alias;
18505  }
18506 
18515  public function getAliasNbPages() {
18516  if ($this->isUnicodeFont()) {
18517  return '{'.$this->alias_tot_pages.'}';
18518  }
18519  return $this->alias_tot_pages;
18520  }
18521 
18530  public function getAliasNumPage() {
18531  if ($this->isUnicodeFont()) {
18532  return '{'.$this->alias_num_page.'}';
18533  }
18534  return $this->alias_num_page;
18535  }
18536 
18545  public function getPageGroupAlias() {
18546  if ($this->isUnicodeFont()) {
18547  return '{'.$this->alias_group_tot_pages.'}';
18548  }
18549  return $this->alias_group_tot_pages;
18550  }
18551 
18560  public function getPageNumGroupAlias() {
18561  if ($this->isUnicodeFont()) {
18562  return '{'.$this->alias_group_num_page.'}';
18563  }
18564  return $this->alias_group_num_page;
18565  }
18566 
18573  public function getGroupPageNo() {
18574  return $this->pagegroups[$this->currpagegroup];
18575  }
18576 
18583  public function getGroupPageNoFormatted() {
18584  return $this->formatPageNumber($this->getGroupPageNo());
18585  }
18586 
18594  protected function formatPageNumber($num) {
18595  return number_format((float)$num, 0, '', '.');
18596  }
18597 
18606  protected function formatTOCPageNumber($num) {
18607  return number_format((float)$num, 0, '', '.');
18608  }
18609 
18616  public function PageNoFormatted() {
18617  return $this->formatPageNumber($this->PageNo());
18618  }
18619 
18625  protected function _putocg() {
18626  if (empty($this->pdflayers)) {
18627  return;
18628  }
18629  foreach ($this->pdflayers as $key => $layer) {
18630  $this->pdflayers[$key]['objid'] = $this->_newobj();
18631  $out = '<< /Type /OCG';
18632  $out .= ' /Name '.$this->_textstring($layer['name'], $this->pdflayers[$key]['objid']);
18633  $out .= ' /Usage <<';
18634  $out .= ' /Print <</PrintState /'.($layer['print']?'ON':'OFF').'>>';
18635  $out .= ' /View <</ViewState /'.($layer['view']?'ON':'OFF').'>>';
18636  $out .= ' >> >>';
18637  $out .= "\n".'endobj';
18638  $this->_out($out);
18639  }
18640  }
18641 
18650  public function startLayer($name='', $print=true, $view=true) {
18651  if ($this->state != 2) {
18652  return;
18653  }
18654  $layer = sprintf('LYR%03d', (count($this->pdflayers) + 1));
18655  if (empty($name)) {
18656  $name = $layer;
18657  } else {
18658  $name = preg_replace('/[^a-zA-Z0-9_\-]/', '', $name);
18659  }
18660  $this->pdflayers[] = array('layer' => $layer, 'name' => $name, 'print' => $print, 'view' => $view);
18661  $this->openMarkedContent = true;
18662  $this->_out('/OC /'.$layer.' BDC');
18663  }
18664 
18670  public function endLayer() {
18671  if ($this->state != 2) {
18672  return;
18673  }
18674  if ($this->openMarkedContent) {
18675  // close existing open marked-content layer
18676  $this->_out('EMC');
18677  $this->openMarkedContent = false;
18678  }
18679  }
18680 
18689  public function setVisibility($v) {
18690  if ($this->state != 2) {
18691  return;
18692  }
18693  $this->endLayer();
18694  switch($v) {
18695  case 'print': {
18696  $this->startLayer('Print', true, false);
18697  break;
18698  }
18699  case 'view':
18700  case 'screen': {
18701  $this->startLayer('View', false, true);
18702  break;
18703  }
18704  case 'all': {
18705  $this->_out('');
18706  break;
18707  }
18708  default: {
18709  $this->Error('Incorrect visibility: '.$v);
18710  break;
18711  }
18712  }
18713  }
18714 
18722  protected function addExtGState($parms) {
18723  if ($this->pdfa_mode) {
18724  // transparencies are not allowed in PDF/A mode
18725  return;
18726  }
18727  // check if this ExtGState already exist
18728  foreach ($this->extgstates as $i => $ext) {
18729  if ($ext['parms'] == $parms) {
18730  if ($this->inxobj) {
18731  // we are inside an XObject template
18732  $this->xobjects[$this->xobjid]['extgstates'][$i] = $ext;
18733  }
18734  // return reference to existing ExtGState
18735  return $i;
18736  }
18737  }
18738  $n = (count($this->extgstates) + 1);
18739  $this->extgstates[$n] = array('parms' => $parms);
18740  if ($this->inxobj) {
18741  // we are inside an XObject template
18742  $this->xobjects[$this->xobjid]['extgstates'][$n] = $this->extgstates[$n];
18743  }
18744  return $n;
18745  }
18746 
18753  protected function setExtGState($gs) {
18754  if ($this->pdfa_mode OR ($this->state != 2)) {
18755  // transparency is not allowed in PDF/A mode
18756  return;
18757  }
18758  $this->_out(sprintf('/GS%d gs', $gs));
18759  }
18760 
18766  protected function _putextgstates() {
18767  foreach ($this->extgstates as $i => $ext) {
18768  $this->extgstates[$i]['n'] = $this->_newobj();
18769  $out = '<< /Type /ExtGState';
18770  foreach ($ext['parms'] as $k => $v) {
18771  if (is_float($v)) {
18772  $v = sprintf('%F', $v);
18773  } elseif ($v === true) {
18774  $v = 'true';
18775  } elseif ($v === false) {
18776  $v = 'false';
18777  }
18778  $out .= ' /'.$k.' '.$v;
18779  }
18780  $out .= ' >>';
18781  $out .= "\n".'endobj';
18782  $this->_out($out);
18783  }
18784  }
18785 
18795  public function setOverprint($stroking=true, $nonstroking='', $mode=0) {
18796  if ($this->state != 2) {
18797  return;
18798  }
18799  $stroking = $stroking ? true : false;
18800  if ($this->empty_string($nonstroking)) {
18801  // default value if not set
18802  $nonstroking = $stroking;
18803  } else {
18804  $nonstroking = $nonstroking ? true : false;
18805  }
18806  if (($mode != 0) AND ($mode != 1)) {
18807  $mode = 0;
18808  }
18809  $this->overprint = array('OP' => $stroking, 'op' => $nonstroking, 'OPM' => $mode);
18810  $gs = $this->addExtGState($this->overprint);
18811  $this->setExtGState($gs);
18812  }
18813 
18821  public function getOverprint() {
18822  return $this->overprint;
18823  }
18824 
18834  public function setAlpha($stroking=1, $bm='Normal', $nonstroking='', $ais=false) {
18835  if ($this->pdfa_mode) {
18836  // transparency is not allowed in PDF/A mode
18837  return;
18838  }
18839  $stroking = floatval($stroking);
18840  if ($this->empty_string($nonstroking)) {
18841  // default value if not set
18842  $nonstroking = $stroking;
18843  } else {
18844  $nonstroking = floatval($nonstroking);
18845  }
18846  if ($bm[0] == '/') {
18847  // remove trailing slash
18848  $bm = substr($bm, 1);
18849  }
18850  if (!in_array($bm, array('Normal', 'Multiply', 'Screen', 'Overlay', 'Darken', 'Lighten', 'ColorDodge', 'ColorBurn', 'HardLight', 'SoftLight', 'Difference', 'Exclusion', 'Hue', 'Saturation', 'Color', 'Luminosity'))) {
18851  $bm = 'Normal';
18852  }
18853  $ais = $ais ? true : false;
18854  $this->alpha = array('CA' => $stroking, 'ca' => $nonstroking, 'BM' => '/'.$bm, 'AIS' => $ais);
18855  $gs = $this->addExtGState($this->alpha);
18856  $this->setExtGState($gs);
18857  }
18858 
18866  public function getAlpha() {
18867  return $this->alpha;
18868  }
18869 
18876  public function setJPEGQuality($quality) {
18877  if (($quality < 1) OR ($quality > 100)) {
18878  $quality = 75;
18879  }
18880  $this->jpeg_quality = intval($quality);
18881  }
18882 
18889  public function setDefaultTableColumns($cols=4) {
18890  $this->default_table_columns = intval($cols);
18891  }
18892 
18899  public function setCellHeightRatio($h) {
18900  $this->cell_height_ratio = $h;
18901  }
18902 
18908  public function getCellHeightRatio() {
18909  return $this->cell_height_ratio;
18910  }
18911 
18918  public function setPDFVersion($version='1.7') {
18919  if ($this->pdfa_mode) {
18920  // PDF/A mode
18921  $this->PDFVersion = '1.4';
18922  } else {
18923  $this->PDFVersion = $version;
18924  }
18925  }
18926 
18936  public function setViewerPreferences($preferences) {
18937  $this->viewer_preferences = $preferences;
18938  }
18939 
18953  public function colorRegistrationBar($x, $y, $w, $h, $transition=true, $vertical=false, $colors='A,R,G,B,C,M,Y,K') {
18954  $bars = explode(',', $colors);
18955  $numbars = count($bars); // number of bars to print
18956  // set bar measures
18957  if ($vertical) {
18958  $coords = array(0, 0, 0, 1);
18959  $wb = $w / $numbars; // bar width
18960  $hb = $h; // bar height
18961  $xd = $wb; // delta x
18962  $yd = 0; // delta y
18963  } else {
18964  $coords = array(1, 0, 0, 0);
18965  $wb = $w; // bar width
18966  $hb = $h / $numbars; // bar height
18967  $xd = 0; // delta x
18968  $yd = $hb; // delta y
18969  }
18970  $xb = $x;
18971  $yb = $y;
18972  foreach ($bars as $col) {
18973  switch ($col) {
18974  // set transition colors
18975  case 'A': { // BLACK
18976  $col_a = array(255);
18977  $col_b = array(0);
18978  break;
18979  }
18980  case 'W': { // WHITE
18981  $col_a = array(0);
18982  $col_b = array(255);
18983  break;
18984  }
18985  case 'R': { // R
18986  $col_a = array(255,255,255);
18987  $col_b = array(255,0,0);
18988  break;
18989  }
18990  case 'G': { // G
18991  $col_a = array(255,255,255);
18992  $col_b = array(0,255,0);
18993  break;
18994  }
18995  case 'B': { // B
18996  $col_a = array(255,255,255);
18997  $col_b = array(0,0,255);
18998  break;
18999  }
19000  case 'C': { // C
19001  $col_a = array(0,0,0,0);
19002  $col_b = array(100,0,0,0);
19003  break;
19004  }
19005  case 'M': { // M
19006  $col_a = array(0,0,0,0);
19007  $col_b = array(0,100,0,0);
19008  break;
19009  }
19010  case 'Y': { // Y
19011  $col_a = array(0,0,0,0);
19012  $col_b = array(0,0,100,0);
19013  break;
19014  }
19015  case 'K': { // K
19016  $col_a = array(0,0,0,0);
19017  $col_b = array(0,0,0,100);
19018  break;
19019  }
19020  default: { // GRAY
19021  $col_a = array(255);
19022  $col_b = array(0);
19023  break;
19024  }
19025  }
19026  if ($transition) {
19027  // color gradient
19028  $this->LinearGradient($xb, $yb, $wb, $hb, $col_a, $col_b, $coords);
19029  } else {
19030  // color rectangle
19031  $this->SetFillColorArray($col_b);
19032  $this->Rect($xb, $yb, $wb, $hb, 'F', array());
19033  }
19034  $xb += $xd;
19035  $yb += $yd;
19036  }
19037  }
19038 
19051  public function cropMark($x, $y, $w, $h, $type='T,R,B,L', $color=array(0,0,0)) {
19052  $this->SetLineStyle(array('width' => (0.5 / $this->k), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $color));
19053  $type = strtoupper($type);
19054  $type = preg_replace('/[^A-Z\-\,]*/', '', $type);
19055  // split type in single components
19056  $type = str_replace('-', ',', $type);
19057  $type = str_replace('TL', 'T,L', $type);
19058  $type = str_replace('TR', 'T,R', $type);
19059  $type = str_replace('BL', 'F,L', $type);
19060  $type = str_replace('BR', 'F,R', $type);
19061  $type = str_replace('A', 'T,L', $type);
19062  $type = str_replace('B', 'T,R', $type);
19063  $type = str_replace('T,RO', 'BO', $type);
19064  $type = str_replace('C', 'F,L', $type);
19065  $type = str_replace('D', 'F,R', $type);
19066  $crops = explode(',', strtoupper($type));
19067  // remove duplicates
19068  $crops = array_unique($crops);
19069  $dw = ($w / 4); // horizontal space to leave before the intersection point
19070  $dh = ($h / 4); // vertical space to leave before the intersection point
19071  foreach ($crops as $crop) {
19072  switch ($crop) {
19073  case 'T':
19074  case 'TOP': {
19075  $x1 = $x;
19076  $y1 = ($y - $h);
19077  $x2 = $x;
19078  $y2 = ($y - $dh);
19079  break;
19080  }
19081  case 'F':
19082  case 'BOTTOM': {
19083  $x1 = $x;
19084  $y1 = ($y + $dh);
19085  $x2 = $x;
19086  $y2 = ($y + $h);
19087  break;
19088  }
19089  case 'L':
19090  case 'LEFT': {
19091  $x1 = ($x - $w);
19092  $y1 = $y;
19093  $x2 = ($x - $dw);
19094  $y2 = $y;
19095  break;
19096  }
19097  case 'R':
19098  case 'RIGHT': {
19099  $x1 = ($x + $dw);
19100  $y1 = $y;
19101  $x2 = ($x + $w);
19102  $y2 = $y;
19103  break;
19104  }
19105  }
19106  $this->Line($x1, $y1, $x2, $y2);
19107  }
19108  }
19109 
19122  public function registrationMark($x, $y, $r, $double=false, $cola=array(0,0,0), $colb=array(255,255,255)) {
19123  $line_style = array('width' => (0.5 / $this->k), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $cola);
19124  $this->SetFillColorArray($cola);
19125  $this->PieSector($x, $y, $r, 90, 180, 'F');
19126  $this->PieSector($x, $y, $r, 270, 360, 'F');
19127  $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8);
19128  if ($double) {
19129  $r2 = $r * 0.5;
19130  $this->SetFillColorArray($colb);
19131  $this->PieSector($x, $y, $r2, 90, 180, 'F');
19132  $this->PieSector($x, $y, $r2, 270, 360, 'F');
19133  $this->SetFillColorArray($cola);
19134  $this->PieSector($x, $y, $r2, 0, 90, 'F');
19135  $this->PieSector($x, $y, $r2, 180, 270, 'F');
19136  $this->Circle($x, $y, $r2, 0, 360, 'C', $line_style, array(), 8);
19137  }
19138  }
19139 
19153  public function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) {
19154  $this->Clip($x, $y, $w, $h);
19155  $this->Gradient(2, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
19156  }
19157 
19171  public function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) {
19172  $this->Clip($x, $y, $w, $h);
19173  $this->Gradient(3, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
19174  }
19175 
19194  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) {
19195  if ($this->pdfa_mode OR ($this->state != 2)) {
19196  return;
19197  }
19198  $this->Clip($x, $y, $w, $h);
19199  $n = count($this->gradients) + 1;
19200  $this->gradients[$n] = array();
19201  $this->gradients[$n]['type'] = 6; //coons patch mesh
19202  $this->gradients[$n]['coords'] = array();
19203  $this->gradients[$n]['antialias'] = $antialias;
19204  $this->gradients[$n]['colors'] = array();
19205  $this->gradients[$n]['transparency'] = false;
19206  //check the coords array if it is the simple array or the multi patch array
19207  if (!isset($coords[0]['f'])) {
19208  //simple array -> convert to multi patch array
19209  if (!isset($col1[1])) {
19210  $col1[1] = $col1[2] = $col1[0];
19211  }
19212  if (!isset($col2[1])) {
19213  $col2[1] = $col2[2] = $col2[0];
19214  }
19215  if (!isset($col3[1])) {
19216  $col3[1] = $col3[2] = $col3[0];
19217  }
19218  if (!isset($col4[1])) {
19219  $col4[1] = $col4[2] = $col4[0];
19220  }
19221  $patch_array[0]['f'] = 0;
19222  $patch_array[0]['points'] = $coords;
19223  $patch_array[0]['colors'][0]['r'] = $col1[0];
19224  $patch_array[0]['colors'][0]['g'] = $col1[1];
19225  $patch_array[0]['colors'][0]['b'] = $col1[2];
19226  $patch_array[0]['colors'][1]['r'] = $col2[0];
19227  $patch_array[0]['colors'][1]['g'] = $col2[1];
19228  $patch_array[0]['colors'][1]['b'] = $col2[2];
19229  $patch_array[0]['colors'][2]['r'] = $col3[0];
19230  $patch_array[0]['colors'][2]['g'] = $col3[1];
19231  $patch_array[0]['colors'][2]['b'] = $col3[2];
19232  $patch_array[0]['colors'][3]['r'] = $col4[0];
19233  $patch_array[0]['colors'][3]['g'] = $col4[1];
19234  $patch_array[0]['colors'][3]['b'] = $col4[2];
19235  } else {
19236  //multi patch array
19237  $patch_array = $coords;
19238  }
19239  $bpcd = 65535; //16 bits per coordinate
19240  //build the data stream
19241  $this->gradients[$n]['stream'] = '';
19242  $count_patch = count($patch_array);
19243  for ($i=0; $i < $count_patch; ++$i) {
19244  $this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit
19245  $count_points = count($patch_array[$i]['points']);
19246  for ($j=0; $j < $count_points; ++$j) {
19247  //each point as 16 bit
19248  $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j] - $coords_min) / ($coords_max - $coords_min)) * $bpcd;
19249  if ($patch_array[$i]['points'][$j] < 0) {
19250  $patch_array[$i]['points'][$j] = 0;
19251  }
19252  if ($patch_array[$i]['points'][$j] > $bpcd) {
19253  $patch_array[$i]['points'][$j] = $bpcd;
19254  }
19255  $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] / 256));
19256  $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] % 256));
19257  }
19258  $count_cols = count($patch_array[$i]['colors']);
19259  for ($j=0; $j < $count_cols; ++$j) {
19260  //each color component as 8 bit
19261  $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']);
19262  $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']);
19263  $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']);
19264  }
19265  }
19266  //paint the gradient
19267  $this->_out('/Sh'.$n.' sh');
19268  //restore previous Graphic State
19269  $this->_out('Q');
19270  if ($this->inxobj) {
19271  // we are inside an XObject template
19272  $this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n];
19273  }
19274  }
19275 
19286  protected function Clip($x, $y, $w, $h) {
19287  if ($this->state != 2) {
19288  return;
19289  }
19290  if ($this->rtl) {
19291  $x = $this->w - $x - $w;
19292  }
19293  //save current Graphic State
19294  $s = 'q';
19295  //set clipping area
19296  $s .= sprintf(' %F %F %F %F re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k);
19297  //set up transformation matrix for gradient
19298  $s .= sprintf(' %F 0 0 %F %F %F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k);
19299  $this->_out($s);
19300  }
19301 
19313  public function Gradient($type, $coords, $stops, $background=array(), $antialias=false) {
19314  if ($this->pdfa_mode OR ($this->state != 2)) {
19315  return;
19316  }
19317  $n = count($this->gradients) + 1;
19318  $this->gradients[$n] = array();
19319  $this->gradients[$n]['type'] = $type;
19320  $this->gradients[$n]['coords'] = $coords;
19321  $this->gradients[$n]['antialias'] = $antialias;
19322  $this->gradients[$n]['colors'] = array();
19323  $this->gradients[$n]['transparency'] = false;
19324  // color space
19325  $numcolspace = count($stops[0]['color']);
19326  $bcolor = array_values($background);
19327  switch($numcolspace) {
19328  case 4: { // CMYK
19329  $this->gradients[$n]['colspace'] = 'DeviceCMYK';
19330  if (!empty($background)) {
19331  $this->gradients[$n]['background'] = sprintf('%F %F %F %F', $bcolor[0]/100, $bcolor[1]/100, $bcolor[2]/100, $bcolor[3]/100);
19332  }
19333  break;
19334  }
19335  case 3: { // RGB
19336  $this->gradients[$n]['colspace'] = 'DeviceRGB';
19337  if (!empty($background)) {
19338  $this->gradients[$n]['background'] = sprintf('%F %F %F', $bcolor[0]/255, $bcolor[1]/255, $bcolor[2]/255);
19339  }
19340  break;
19341  }
19342  case 1: { // Gray scale
19343  $this->gradients[$n]['colspace'] = 'DeviceGray';
19344  if (!empty($background)) {
19345  $this->gradients[$n]['background'] = sprintf('%F', $bcolor[0]/255);
19346  }
19347  break;
19348  }
19349  }
19350  $num_stops = count($stops);
19351  $last_stop_id = $num_stops - 1;
19352  foreach ($stops as $key => $stop) {
19353  $this->gradients[$n]['colors'][$key] = array();
19354  // offset represents a location along the gradient vector
19355  if (isset($stop['offset'])) {
19356  $this->gradients[$n]['colors'][$key]['offset'] = $stop['offset'];
19357  } else {
19358  if ($key == 0) {
19359  $this->gradients[$n]['colors'][$key]['offset'] = 0;
19360  } elseif ($key == $last_stop_id) {
19361  $this->gradients[$n]['colors'][$key]['offset'] = 1;
19362  } else {
19363  $offsetstep = (1 - $this->gradients[$n]['colors'][($key - 1)]['offset']) / ($num_stops - $key);
19364  $this->gradients[$n]['colors'][$key]['offset'] = $this->gradients[$n]['colors'][($key - 1)]['offset'] + $offsetstep;
19365  }
19366  }
19367  if (isset($stop['opacity'])) {
19368  $this->gradients[$n]['colors'][$key]['opacity'] = $stop['opacity'];
19369  if ((!$this->pdfa_mode) AND ($stop['opacity'] < 1)) {
19370  $this->gradients[$n]['transparency'] = true;
19371  }
19372  } else {
19373  $this->gradients[$n]['colors'][$key]['opacity'] = 1;
19374  }
19375  // exponent for the exponential interpolation function
19376  if (isset($stop['exponent'])) {
19377  $this->gradients[$n]['colors'][$key]['exponent'] = $stop['exponent'];
19378  } else {
19379  $this->gradients[$n]['colors'][$key]['exponent'] = 1;
19380  }
19381  // set colors
19382  $color = array_values($stop['color']);
19383  switch($numcolspace) {
19384  case 4: { // CMYK
19385  $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F %F %F %F', $color[0]/100, $color[1]/100, $color[2]/100, $color[3]/100);
19386  break;
19387  }
19388  case 3: { // RGB
19389  $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F %F %F', $color[0]/255, $color[1]/255, $color[2]/255);
19390  break;
19391  }
19392  case 1: { // Gray scale
19393  $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F', $color[0]/255);
19394  break;
19395  }
19396  }
19397  }
19398  if ($this->gradients[$n]['transparency']) {
19399  // paint luminosity gradient
19400  $this->_out('/TGS'.$n.' gs');
19401  }
19402  //paint the gradient
19403  $this->_out('/Sh'.$n.' sh');
19404  //restore previous Graphic State
19405  $this->_out('Q');
19406  if ($this->inxobj) {
19407  // we are inside an XObject template
19408  $this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n];
19409  }
19410  }
19411 
19418  function _putshaders() {
19419  if ($this->pdfa_mode) {
19420  return;
19421  }
19422  $idt = count($this->gradients); //index for transparency gradients
19423  foreach ($this->gradients as $id => $grad) {
19424  if (($grad['type'] == 2) OR ($grad['type'] == 3)) {
19425  $fc = $this->_newobj();
19426  $out = '<<';
19427  $out .= ' /FunctionType 3';
19428  $out .= ' /Domain [0 1]';
19429  $functions = '';
19430  $bounds = '';
19431  $encode = '';
19432  $i = 1;
19433  $num_cols = count($grad['colors']);
19434  $lastcols = $num_cols - 1;
19435  for ($i = 1; $i < $num_cols; ++$i) {
19436  $functions .= ($fc + $i).' 0 R ';
19437  if ($i < $lastcols) {
19438  $bounds .= sprintf('%F ', $grad['colors'][$i]['offset']);
19439  }
19440  $encode .= '0 1 ';
19441  }
19442  $out .= ' /Functions ['.trim($functions).']';
19443  $out .= ' /Bounds ['.trim($bounds).']';
19444  $out .= ' /Encode ['.trim($encode).']';
19445  $out .= ' >>';
19446  $out .= "\n".'endobj';
19447  $this->_out($out);
19448  for ($i = 1; $i < $num_cols; ++$i) {
19449  $this->_newobj();
19450  $out = '<<';
19451  $out .= ' /FunctionType 2';
19452  $out .= ' /Domain [0 1]';
19453  $out .= ' /C0 ['.$grad['colors'][($i - 1)]['color'].']';
19454  $out .= ' /C1 ['.$grad['colors'][$i]['color'].']';
19455  $out .= ' /N '.$grad['colors'][$i]['exponent'];
19456  $out .= ' >>';
19457  $out .= "\n".'endobj';
19458  $this->_out($out);
19459  }
19460  // set transparency fuctions
19461  if ($grad['transparency']) {
19462  $ft = $this->_newobj();
19463  $out = '<<';
19464  $out .= ' /FunctionType 3';
19465  $out .= ' /Domain [0 1]';
19466  $functions = '';
19467  $i = 1;
19468  $num_cols = count($grad['colors']);
19469  for ($i = 1; $i < $num_cols; ++$i) {
19470  $functions .= ($ft + $i).' 0 R ';
19471  }
19472  $out .= ' /Functions ['.trim($functions).']';
19473  $out .= ' /Bounds ['.trim($bounds).']';
19474  $out .= ' /Encode ['.trim($encode).']';
19475  $out .= ' >>';
19476  $out .= "\n".'endobj';
19477  $this->_out($out);
19478  for ($i = 1; $i < $num_cols; ++$i) {
19479  $this->_newobj();
19480  $out = '<<';
19481  $out .= ' /FunctionType 2';
19482  $out .= ' /Domain [0 1]';
19483  $out .= ' /C0 ['.$grad['colors'][($i - 1)]['opacity'].']';
19484  $out .= ' /C1 ['.$grad['colors'][$i]['opacity'].']';
19485  $out .= ' /N '.$grad['colors'][$i]['exponent'];
19486  $out .= ' >>';
19487  $out .= "\n".'endobj';
19488  $this->_out($out);
19489  }
19490  }
19491  }
19492  // set shading object
19493  $this->_newobj();
19494  $out = '<< /ShadingType '.$grad['type'];
19495  if (isset($grad['colspace'])) {
19496  $out .= ' /ColorSpace /'.$grad['colspace'];
19497  } else {
19498  $out .= ' /ColorSpace /DeviceRGB';
19499  }
19500  if (isset($grad['background']) AND !empty($grad['background'])) {
19501  $out .= ' /Background ['.$grad['background'].']';
19502  }
19503  if (isset($grad['antialias']) AND ($grad['antialias'] === true)) {
19504  $out .= ' /AntiAlias true';
19505  }
19506  if ($grad['type'] == 2) {
19507  $out .= ' '.sprintf('/Coords [%F %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]);
19508  $out .= ' /Domain [0 1]';
19509  $out .= ' /Function '.$fc.' 0 R';
19510  $out .= ' /Extend [true true]';
19511  $out .= ' >>';
19512  } elseif ($grad['type'] == 3) {
19513  //x0, y0, r0, x1, y1, r1
19514  //at this this time radius of inner circle is 0
19515  $out .= ' '.sprintf('/Coords [%F %F 0 %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]);
19516  $out .= ' /Domain [0 1]';
19517  $out .= ' /Function '.$fc.' 0 R';
19518  $out .= ' /Extend [true true]';
19519  $out .= ' >>';
19520  } elseif ($grad['type'] == 6) {
19521  $out .= ' /BitsPerCoordinate 16';
19522  $out .= ' /BitsPerComponent 8';
19523  $out .= ' /Decode[0 1 0 1 0 1 0 1 0 1]';
19524  $out .= ' /BitsPerFlag 8';
19525  $stream = $this->_getrawstream($grad['stream']);
19526  $out .= ' /Length '.strlen($stream);
19527  $out .= ' >>';
19528  $out .= ' stream'."\n".$stream."\n".'endstream';
19529  }
19530  $out .= "\n".'endobj';
19531  $this->_out($out);
19532  if ($grad['transparency']) {
19533  $shading_transparency = preg_replace('/\/ColorSpace \/[^\s]+/si', '/ColorSpace /DeviceGray', $out);
19534  $shading_transparency = preg_replace('/\/Function [0-9]+ /si', '/Function '.$ft.' ', $shading_transparency);
19535  }
19536  $this->gradients[$id]['id'] = $this->n;
19537  // set pattern object
19538  $this->_newobj();
19539  $out = '<< /Type /Pattern /PatternType 2';
19540  $out .= ' /Shading '.$this->gradients[$id]['id'].' 0 R';
19541  $out .= ' >>';
19542  $out .= "\n".'endobj';
19543  $this->_out($out);
19544  $this->gradients[$id]['pattern'] = $this->n;
19545  // set shading and pattern for transparency mask
19546  if ($grad['transparency']) {
19547  // luminosity pattern
19548  $idgs = $id + $idt;
19549  $this->_newobj();
19550  $this->_out($shading_transparency);
19551  $this->gradients[$idgs]['id'] = $this->n;
19552  $this->_newobj();
19553  $out = '<< /Type /Pattern /PatternType 2';
19554  $out .= ' /Shading '.$this->gradients[$idgs]['id'].' 0 R';
19555  $out .= ' >>';
19556  $out .= "\n".'endobj';
19557  $this->_out($out);
19558  $this->gradients[$idgs]['pattern'] = $this->n;
19559  // luminosity XObject
19560  $oid = $this->_newobj();
19561  $this->xobjects['LX'.$oid] = array('n' => $oid);
19562  $filter = '';
19563  $stream = 'q /a0 gs /Pattern cs /p'.$idgs.' scn 0 0 '.$this->wPt.' '.$this->hPt.' re f Q';
19564  if ($this->compress) {
19565  $filter = ' /Filter /FlateDecode';
19566  $stream = gzcompress($stream);
19567  }
19568  $stream = $this->_getrawstream($stream);
19569  $out = '<< /Type /XObject /Subtype /Form /FormType 1'.$filter;
19570  $out .= ' /Length '.strlen($stream);
19571  $rect = sprintf('%F %F', $this->wPt, $this->hPt);
19572  $out .= ' /BBox [0 0 '.$rect.']';
19573  $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceGray >>';
19574  $out .= ' /Resources <<';
19575  $out .= ' /ExtGState << /a0 << /ca 1 /CA 1 >> >>';
19576  $out .= ' /Pattern << /p'.$idgs.' '.$this->gradients[$idgs]['pattern'].' 0 R >>';
19577  $out .= ' >>';
19578  $out .= ' >> ';
19579  $out .= ' stream'."\n".$stream."\n".'endstream';
19580  $out .= "\n".'endobj';
19581  $this->_out($out);
19582  // SMask
19583  $this->_newobj();
19584  $out = '<< /Type /Mask /S /Luminosity /G '.($this->n - 1).' 0 R >>'."\n".'endobj';
19585  $this->_out($out);
19586  // ExtGState
19587  $this->_newobj();
19588  $out = '<< /Type /ExtGState /SMask '.($this->n - 1).' 0 R /AIS false >>'."\n".'endobj';
19589  $this->_out($out);
19590  $this->extgstates[] = array('n' => $this->n, 'name' => 'TGS'.$id);
19591  }
19592  }
19593  }
19594 
19610  public function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) {
19611  $this->PieSectorXY($xc, $yc, $r, $r, $a, $b, $style, $cw, $o);
19612  }
19613 
19631  public function PieSectorXY($xc, $yc, $rx, $ry, $a, $b, $style='FD', $cw=false, $o=0, $nc=2) {
19632  if ($this->state != 2) {
19633  return;
19634  }
19635  if ($this->rtl) {
19636  $xc = ($this->w - $xc);
19637  }
19638  $op = $this->getPathPaintOperator($style);
19639  if ($op == 'f') {
19640  $line_style = array();
19641  }
19642  if ($cw) {
19643  $d = $b;
19644  $b = (360 - $a + $o);
19645  $a = (360 - $d + $o);
19646  } else {
19647  $b += $o;
19648  $a += $o;
19649  }
19650  $this->_outellipticalarc($xc, $yc, $rx, $ry, 0, $a, $b, true, $nc);
19651  $this->_out($op);
19652  }
19653 
19675  public function ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0, $fitonpage=false, $fixoutvals=false) {
19676  if ($this->state != 2) {
19677  return;
19678  }
19679  if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) {
19680  // convert EPS to raster image using GD or ImageMagick libraries
19681  return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
19682  }
19683  if ($x === '') {
19684  $x = $this->x;
19685  }
19686  if ($y === '') {
19687  $y = $this->y;
19688  }
19689  // check page for no-write regions and adapt page margins if necessary
19690  list($x, $y) = $this->checkPageRegions($h, $x, $y);
19691  $k = $this->k;
19692  if ($file{0} === '@') { // image from string
19693  $data = substr($file, 1);
19694  } else { // EPS/AI file
19695  $data = file_get_contents($file);
19696  }
19697  if ($data === false) {
19698  $this->Error('EPS file not found: '.$file);
19699  }
19700  $regs = array();
19701  // EPS/AI compatibility check (only checks files created by Adobe Illustrator!)
19702  preg_match("/%%Creator:([^\r\n]+)/", $data, $regs); # find Creator
19703  if (count($regs) > 1) {
19704  $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0"
19705  if (strpos($version_str, 'Adobe Illustrator') !== false) {
19706  $versexp = explode(' ', $version_str);
19707  $version = (float)array_pop($versexp);
19708  if ($version >= 9) {
19709  $this->Error('This version of Adobe Illustrator file is not supported: '.$file);
19710  }
19711  }
19712  }
19713  // strip binary bytes in front of PS-header
19714  $start = strpos($data, '%!PS-Adobe');
19715  if ($start > 0) {
19716  $data = substr($data, $start);
19717  }
19718  // find BoundingBox params
19719  preg_match("/%%BoundingBox:([^\r\n]+)/", $data, $regs);
19720  if (count($regs) > 1) {
19721  list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1]));
19722  } else {
19723  $this->Error('No BoundingBox found in EPS/AI file: '.$file);
19724  }
19725  $start = strpos($data, '%%EndSetup');
19726  if ($start === false) {
19727  $start = strpos($data, '%%EndProlog');
19728  }
19729  if ($start === false) {
19730  $start = strpos($data, '%%BoundingBox');
19731  }
19732  $data = substr($data, $start);
19733  $end = strpos($data, '%%PageTrailer');
19734  if ($end===false) {
19735  $end = strpos($data, 'showpage');
19736  }
19737  if ($end) {
19738  $data = substr($data, 0, $end);
19739  }
19740  // calculate image width and height on document
19741  if (($w <= 0) AND ($h <= 0)) {
19742  $w = ($x2 - $x1) / $k;
19743  $h = ($y2 - $y1) / $k;
19744  } elseif ($w <= 0) {
19745  $w = ($x2-$x1) / $k * ($h / (($y2 - $y1) / $k));
19746  } elseif ($h <= 0) {
19747  $h = ($y2 - $y1) / $k * ($w / (($x2 - $x1) / $k));
19748  }
19749  // fit the image on available space
19750  list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
19751  if ($this->rasterize_vector_images) {
19752  // convert EPS to raster image using GD or ImageMagick libraries
19753  return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
19754  }
19755  // set scaling factors
19756  $scale_x = $w / (($x2 - $x1) / $k);
19757  $scale_y = $h / (($y2 - $y1) / $k);
19758  // set alignment
19759  $this->img_rb_y = $y + $h;
19760  // set alignment
19761  if ($this->rtl) {
19762  if ($palign == 'L') {
19763  $ximg = $this->lMargin;
19764  } elseif ($palign == 'C') {
19765  $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
19766  } elseif ($palign == 'R') {
19767  $ximg = $this->w - $this->rMargin - $w;
19768  } else {
19769  $ximg = $x - $w;
19770  }
19771  $this->img_rb_x = $ximg;
19772  } else {
19773  if ($palign == 'L') {
19774  $ximg = $this->lMargin;
19775  } elseif ($palign == 'C') {
19776  $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
19777  } elseif ($palign == 'R') {
19778  $ximg = $this->w - $this->rMargin - $w;
19779  } else {
19780  $ximg = $x;
19781  }
19782  $this->img_rb_x = $ximg + $w;
19783  }
19784  if ($useBoundingBox) {
19785  $dx = $ximg * $k - $x1;
19786  $dy = $y * $k - $y1;
19787  } else {
19788  $dx = $ximg * $k;
19789  $dy = $y * $k;
19790  }
19791  // save the current graphic state
19792  $this->_out('q'.$this->epsmarker);
19793  // translate
19794  $this->_out(sprintf('%F %F %F %F %F %F cm', 1, 0, 0, 1, $dx, $dy + ($this->hPt - (2 * $y * $k) - ($y2 - $y1))));
19795  // scale
19796  if (isset($scale_x)) {
19797  $this->_out(sprintf('%F %F %F %F %F %F cm', $scale_x, 0, 0, $scale_y, $x1 * (1 - $scale_x), $y2 * (1 - $scale_y)));
19798  }
19799  // handle pc/unix/mac line endings
19800  $lines = preg_split('/[\r\n]+/si', $data, -1, PREG_SPLIT_NO_EMPTY);
19801  $u=0;
19802  $cnt = count($lines);
19803  for ($i=0; $i < $cnt; ++$i) {
19804  $line = $lines[$i];
19805  if (($line == '') OR ($line{0} == '%')) {
19806  continue;
19807  }
19808  $len = strlen($line);
19809  // check for spot color names
19810  $color_name = '';
19811  if (strcasecmp('x', substr(trim($line), -1)) == 0) {
19812  if (preg_match('/\([^\)]*\)/', $line, $matches) > 0) {
19813  // extract spot color name
19814  $color_name = $matches[0];
19815  // remove color name from string
19816  $line = str_replace(' '.$color_name, '', $line);
19817  // remove pharentesis from color name
19818  $color_name = substr($color_name, 1, -1);
19819  }
19820  }
19821  $chunks = explode(' ', $line);
19822  $cmd = trim(array_pop($chunks));
19823  // RGB
19824  if (($cmd == 'Xa') OR ($cmd == 'XA')) {
19825  $b = array_pop($chunks);
19826  $g = array_pop($chunks);
19827  $r = array_pop($chunks);
19828  $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!
19829  continue;
19830  }
19831  $skip = false;
19832  if ($fixoutvals) {
19833  // check for values outside the bounding box
19834  switch ($cmd) {
19835  case 'm':
19836  case 'l':
19837  case 'L': {
19838  // skip values outside bounding box
19839  foreach ($chunks as $key => $val) {
19840  if ((($key % 2) == 0) AND (($val < $x1) OR ($val > $x2))) {
19841  $skip = true;
19842  } elseif ((($key % 2) != 0) AND (($val < $y1) OR ($val > $y2))) {
19843  $skip = true;
19844  }
19845  }
19846  }
19847  }
19848  }
19849  switch ($cmd) {
19850  case 'm':
19851  case 'l':
19852  case 'v':
19853  case 'y':
19854  case 'c':
19855  case 'k':
19856  case 'K':
19857  case 'g':
19858  case 'G':
19859  case 's':
19860  case 'S':
19861  case 'J':
19862  case 'j':
19863  case 'w':
19864  case 'M':
19865  case 'd':
19866  case 'n': {
19867  if ($skip) {
19868  break;
19869  }
19870  $this->_out($line);
19871  break;
19872  }
19873  case 'x': {// custom fill color
19874  if (empty($color_name)) {
19875  // CMYK color
19876  list($col_c, $col_m, $col_y, $col_k) = $chunks;
19877  $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' k');
19878  } else {
19879  // Spot Color (CMYK + tint)
19880  list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
19881  $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
19882  $color_cmd = sprintf('/CS%d cs %F scn', $this->spot_colors[$color_name]['i'], (1 - $col_t));
19883  $this->_out($color_cmd);
19884  }
19885  break;
19886  }
19887  case 'X': { // custom stroke color
19888  if (empty($color_name)) {
19889  // CMYK color
19890  list($col_c, $col_m, $col_y, $col_k) = $chunks;
19891  $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' K');
19892  } else {
19893  // Spot Color (CMYK + tint)
19894  list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
19895  $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
19896  $color_cmd = sprintf('/CS%d CS %F SCN', $this->spot_colors[$color_name]['i'], (1 - $col_t));
19897  $this->_out($color_cmd);
19898  }
19899  break;
19900  }
19901  case 'Y':
19902  case 'N':
19903  case 'V':
19904  case 'L':
19905  case 'C': {
19906  if ($skip) {
19907  break;
19908  }
19909  $line[($len - 1)] = strtolower($cmd);
19910  $this->_out($line);
19911  break;
19912  }
19913  case 'b':
19914  case 'B': {
19915  $this->_out($cmd . '*');
19916  break;
19917  }
19918  case 'f':
19919  case 'F': {
19920  if ($u > 0) {
19921  $isU = false;
19922  $max = min(($i + 5), $cnt);
19923  for ($j = ($i + 1); $j < $max; ++$j) {
19924  $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U')));
19925  }
19926  if ($isU) {
19927  $this->_out('f*');
19928  }
19929  } else {
19930  $this->_out('f*');
19931  }
19932  break;
19933  }
19934  case '*u': {
19935  ++$u;
19936  break;
19937  }
19938  case '*U': {
19939  --$u;
19940  break;
19941  }
19942  }
19943  }
19944  // restore previous graphic state
19945  $this->_out($this->epsmarker.'Q');
19946  if (!empty($border)) {
19947  $bx = $this->x;
19948  $by = $this->y;
19949  $this->x = $ximg;
19950  if ($this->rtl) {
19951  $this->x += $w;
19952  }
19953  $this->y = $y;
19954  $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
19955  $this->x = $bx;
19956  $this->y = $by;
19957  }
19958  if ($link) {
19959  $this->Link($ximg, $y, $w, $h, $link, 0);
19960  }
19961  // set pointer to align the next text/objects
19962  switch($align) {
19963  case 'T':{
19964  $this->y = $y;
19965  $this->x = $this->img_rb_x;
19966  break;
19967  }
19968  case 'M':{
19969  $this->y = $y + round($h/2);
19970  $this->x = $this->img_rb_x;
19971  break;
19972  }
19973  case 'B':{
19974  $this->y = $this->img_rb_y;
19975  $this->x = $this->img_rb_x;
19976  break;
19977  }
19978  case 'N':{
19979  $this->SetY($this->img_rb_y);
19980  break;
19981  }
19982  default:{
19983  break;
19984  }
19985  }
19986  $this->endlinex = $this->img_rb_x;
19987  }
19988 
19994  public function setBarcode($bc='') {
19995  $this->barcode = $bc;
19996  }
19997 
20004  public function getBarcode() {
20005  return $this->barcode;
20006  }
20007 
20038  public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres='', $style='', $align='') {
20039  if ($this->empty_string(trim($code))) {
20040  return;
20041  }
20042  require_once(dirname(__FILE__).'/barcodes.php');
20043  // save current graphic settings
20044  $gvars = $this->getGraphicVars();
20045  // create new barcode object
20046  $barcodeobj = new TCPDFBarcode($code, $type);
20047  $arrcode = $barcodeobj->getBarcodeArray();
20048  if (($arrcode === false) OR empty($arrcode) OR ($arrcode['maxw'] == 0)) {
20049  $this->Error('Error in 1D barcode string');
20050  }
20051  // set default values
20052  if (!isset($style['position'])) {
20053  $style['position'] = '';
20054  } elseif ($style['position'] == 'S') {
20055  // keep this for backward compatibility
20056  $style['position'] = '';
20057  $style['stretch'] = true;
20058  }
20059  if (!isset($style['fitwidth'])) {
20060  if (!isset($style['stretch'])) {
20061  $style['fitwidth'] = true;
20062  } else {
20063  $style['fitwidth'] = false;
20064  }
20065  }
20066  if ($style['fitwidth']) {
20067  // disable stretch
20068  $style['stretch'] = false;
20069  }
20070  if (!isset($style['stretch'])) {
20071  if (($w === '') OR ($w <= 0)) {
20072  $style['stretch'] = false;
20073  } else {
20074  $style['stretch'] = true;
20075  }
20076  }
20077  if (!isset($style['fgcolor'])) {
20078  $style['fgcolor'] = array(0,0,0); // default black
20079  }
20080  if (!isset($style['bgcolor'])) {
20081  $style['bgcolor'] = false; // default transparent
20082  }
20083  if (!isset($style['border'])) {
20084  $style['border'] = false;
20085  }
20086  $fontsize = 0;
20087  if (!isset($style['text'])) {
20088  $style['text'] = false;
20089  }
20090  if ($style['text'] AND isset($style['font'])) {
20091  if (isset($style['fontsize'])) {
20092  $fontsize = $style['fontsize'];
20093  }
20094  $this->SetFont($style['font'], '', $fontsize);
20095  }
20096  if (!isset($style['stretchtext'])) {
20097  $style['stretchtext'] = 4;
20098  }
20099  if ($x === '') {
20100  $x = $this->x;
20101  }
20102  if ($y === '') {
20103  $y = $this->y;
20104  }
20105  // check page for no-write regions and adapt page margins if necessary
20106  list($x, $y) = $this->checkPageRegions($h, $x, $y);
20107  if (($w === '') OR ($w <= 0)) {
20108  if ($this->rtl) {
20109  $w = $x - $this->lMargin;
20110  } else {
20111  $w = $this->w - $this->rMargin - $x;
20112  }
20113  }
20114  // padding
20115  if (!isset($style['padding'])) {
20116  $padding = 0;
20117  } elseif ($style['padding'] === 'auto') {
20118  $padding = 10 * ($w / ($arrcode['maxw'] + 20));
20119  } else {
20120  $padding = floatval($style['padding']);
20121  }
20122  // horizontal padding
20123  if (!isset($style['hpadding'])) {
20124  $hpadding = $padding;
20125  } elseif ($style['hpadding'] === 'auto') {
20126  $hpadding = 10 * ($w / ($arrcode['maxw'] + 20));
20127  } else {
20128  $hpadding = floatval($style['hpadding']);
20129  }
20130  // vertical padding
20131  if (!isset($style['vpadding'])) {
20132  $vpadding = $padding;
20133  } elseif ($style['vpadding'] === 'auto') {
20134  $vpadding = ($hpadding / 2);
20135  } else {
20136  $vpadding = floatval($style['vpadding']);
20137  }
20138  // calculate xres (single bar width)
20139  $max_xres = ($w - (2 * $hpadding)) / $arrcode['maxw'];
20140  if ($style['stretch']) {
20141  $xres = $max_xres;
20142  } else {
20143  if ($this->empty_string($xres)) {
20144  $xres = (0.141 * $this->k); // default bar width = 0.4 mm
20145  }
20146  if ($xres > $max_xres) {
20147  // correct xres to fit on $w
20148  $xres = $max_xres;
20149  }
20150  if ((isset($style['padding']) AND ($style['padding'] === 'auto'))
20151  OR (isset($style['hpadding']) AND ($style['hpadding'] === 'auto'))) {
20152  $hpadding = 10 * $xres;
20153  if (isset($style['vpadding']) AND ($style['vpadding'] === 'auto')) {
20154  $vpadding = ($hpadding / 2);
20155  }
20156  }
20157  }
20158  if ($style['fitwidth']) {
20159  $wold = $w;
20160  $w = (($arrcode['maxw'] * $xres) + (2 * $hpadding));
20161  if (isset($style['cellfitalign'])) {
20162  switch ($style['cellfitalign']) {
20163  case 'L': {
20164  if ($this->rtl) {
20165  $x -= ($wold - $w);
20166  }
20167  break;
20168  }
20169  case 'R': {
20170  if (!$this->rtl) {
20171  $x += ($wold - $w);
20172  }
20173  break;
20174  }
20175  case 'C': {
20176  if ($this->rtl) {
20177  $x -= (($wold - $w) / 2);
20178  } else {
20179  $x += (($wold - $w) / 2);
20180  }
20181  break;
20182  }
20183  default : {
20184  break;
20185  }
20186  }
20187  }
20188  }
20189  $text_height = ($this->cell_height_ratio * $fontsize / $this->k);
20190  // height
20191  if (($h === '') OR ($h <= 0)) {
20192  // set default height
20193  $h = (($arrcode['maxw'] * $xres) / 3) + (2 * $vpadding) + $text_height;
20194  }
20195  $barh = $h - $text_height - (2 * $vpadding);
20196  if ($barh <=0) {
20197  // try to reduce font or padding to fit barcode on available height
20198  if ($text_height > $h) {
20199  $fontsize = (($h * $this->k) / (4 * $this->cell_height_ratio));
20200  $text_height = ($this->cell_height_ratio * $fontsize / $this->k);
20201  $this->SetFont($style['font'], '', $fontsize);
20202  }
20203  if ($vpadding > 0) {
20204  $vpadding = (($h - $text_height) / 4);
20205  }
20206  $barh = $h - $text_height - (2 * $vpadding);
20207  }
20208  // fit the barcode on available space
20209  list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false);
20210  // set alignment
20211  $this->img_rb_y = $y + $h;
20212  // set alignment
20213  if ($this->rtl) {
20214  if ($style['position'] == 'L') {
20215  $xpos = $this->lMargin;
20216  } elseif ($style['position'] == 'C') {
20217  $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
20218  } elseif ($style['position'] == 'R') {
20219  $xpos = $this->w - $this->rMargin - $w;
20220  } else {
20221  $xpos = $x - $w;
20222  }
20223  $this->img_rb_x = $xpos;
20224  } else {
20225  if ($style['position'] == 'L') {
20226  $xpos = $this->lMargin;
20227  } elseif ($style['position'] == 'C') {
20228  $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
20229  } elseif ($style['position'] == 'R') {
20230  $xpos = $this->w - $this->rMargin - $w;
20231  } else {
20232  $xpos = $x;
20233  }
20234  $this->img_rb_x = $xpos + $w;
20235  }
20236  $xpos_rect = $xpos;
20237  if (!isset($style['align'])) {
20238  $style['align'] = 'C';
20239  }
20240  switch ($style['align']) {
20241  case 'L': {
20242  $xpos = $xpos_rect + $hpadding;
20243  break;
20244  }
20245  case 'R': {
20246  $xpos = $xpos_rect + ($w - ($arrcode['maxw'] * $xres)) - $hpadding;
20247  break;
20248  }
20249  case 'C':
20250  default : {
20251  $xpos = $xpos_rect + (($w - ($arrcode['maxw'] * $xres)) / 2);
20252  break;
20253  }
20254  }
20255  $xpos_text = $xpos;
20256  // barcode is always printed in LTR direction
20257  $tempRTL = $this->rtl;
20258  $this->rtl = false;
20259  // print background color
20260  if ($style['bgcolor']) {
20261  $this->Rect($xpos_rect, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
20262  } elseif ($style['border']) {
20263  $this->Rect($xpos_rect, $y, $w, $h, 'D');
20264  }
20265  // set foreground color
20266  $this->SetDrawColorArray($style['fgcolor']);
20267  $this->SetTextColorArray($style['fgcolor']);
20268  // print bars
20269  foreach ($arrcode['bcode'] as $k => $v) {
20270  $bw = ($v['w'] * $xres);
20271  if ($v['t']) {
20272  // draw a vertical bar
20273  $ypos = $y + $vpadding + ($v['p'] * $barh / $arrcode['maxh']);
20274  $this->Rect($xpos, $ypos, $bw, ($v['h'] * $barh / $arrcode['maxh']), 'F', array(), $style['fgcolor']);
20275  }
20276  $xpos += $bw;
20277  }
20278  // print text
20279  if ($style['text']) {
20280  if (isset($style['label']) AND !$this->empty_string($style['label'])) {
20281  $label = $style['label'];
20282  } else {
20283  $label = $code;
20284  }
20285  $txtwidth = ($arrcode['maxw'] * $xres);
20286  if ($this->GetStringWidth($label) > $txtwidth) {
20287  $style['stretchtext'] = 2;
20288  }
20289  // print text
20290  $this->x = $xpos_text;
20291  $this->y = $y + $vpadding + $barh;
20292  $cellpadding = $this->cell_padding;
20293  $this->SetCellPadding(0);
20294  $this->Cell($txtwidth, '', $label, 0, 0, 'C', false, '', $style['stretchtext'], false, 'T', 'T');
20295  $this->cell_padding = $cellpadding;
20296  }
20297  // restore original direction
20298  $this->rtl = $tempRTL;
20299  // restore previous settings
20300  $this->setGraphicVars($gvars);
20301  // set pointer to align the next text/objects
20302  switch($align) {
20303  case 'T':{
20304  $this->y = $y;
20305  $this->x = $this->img_rb_x;
20306  break;
20307  }
20308  case 'M':{
20309  $this->y = $y + round($h / 2);
20310  $this->x = $this->img_rb_x;
20311  break;
20312  }
20313  case 'B':{
20314  $this->y = $this->img_rb_y;
20315  $this->x = $this->img_rb_x;
20316  break;
20317  }
20318  case 'N':{
20319  $this->SetY($this->img_rb_y);
20320  break;
20321  }
20322  default:{
20323  break;
20324  }
20325  }
20326  $this->endlinex = $this->img_rb_x;
20327  }
20328 
20344  public function writeBarcode($x, $y, $w, $h, $type, $style, $font, $xres, $code) {
20345  // convert old settings for the new write1DBarcode() function.
20346  $xres = 1 / $xres;
20347  $newstyle = array(
20348  'position' => '',
20349  'align' => '',
20350  'stretch' => false,
20351  'fitwidth' => false,
20352  'cellfitalign' => '',
20353  'border' => false,
20354  'padding' => 0,
20355  'fgcolor' => array(0,0,0),
20356  'bgcolor' => false,
20357  'text' => true,
20358  'font' => $font,
20359  'fontsize' => 8,
20360  'stretchtext' => 4
20361  );
20362  if ($style & 1) {
20363  $newstyle['border'] = true;
20364  }
20365  if ($style & 2) {
20366  $newstyle['bgcolor'] = false;
20367  }
20368  if ($style & 4) {
20369  $newstyle['position'] = 'C';
20370  } elseif ($style & 8) {
20371  $newstyle['position'] = 'L';
20372  } elseif ($style & 16) {
20373  $newstyle['position'] = 'R';
20374  }
20375  if ($style & 128) {
20376  $newstyle['text'] = true;
20377  }
20378  if ($style & 256) {
20379  $newstyle['stretchtext'] = 4;
20380  }
20381  $this->write1DBarcode($code, $type, $x, $y, $w, $h, $xres, $newstyle, '');
20382  }
20383 
20409  public function write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='', $distort=false) {
20410  if ($this->empty_string(trim($code))) {
20411  return;
20412  }
20413  require_once(dirname(__FILE__).'/2dbarcodes.php');
20414  // save current graphic settings
20415  $gvars = $this->getGraphicVars();
20416  // create new barcode object
20417  $barcodeobj = new TCPDF2DBarcode($code, $type);
20418  $arrcode = $barcodeobj->getBarcodeArray();
20419  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)) {
20420  $this->Error('Error in 2D barcode string');
20421  }
20422  // set default values
20423  if (!isset($style['position'])) {
20424  $style['position'] = '';
20425  }
20426  if (!isset($style['fgcolor'])) {
20427  $style['fgcolor'] = array(0,0,0); // default black
20428  }
20429  if (!isset($style['bgcolor'])) {
20430  $style['bgcolor'] = false; // default transparent
20431  }
20432  if (!isset($style['border'])) {
20433  $style['border'] = false;
20434  }
20435  // padding
20436  if (!isset($style['padding'])) {
20437  $style['padding'] = 0;
20438  } elseif ($style['padding'] === 'auto') {
20439  $style['padding'] = 4;
20440  }
20441  if (!isset($style['hpadding'])) {
20442  $style['hpadding'] = $style['padding'];
20443  } elseif ($style['hpadding'] === 'auto') {
20444  $style['hpadding'] = 4;
20445  }
20446  if (!isset($style['vpadding'])) {
20447  $style['vpadding'] = $style['padding'];
20448  } elseif ($style['vpadding'] === 'auto') {
20449  $style['vpadding'] = 4;
20450  }
20451  $hpad = (2 * $style['hpadding']);
20452  $vpad = (2 * $style['vpadding']);
20453  // cell (module) dimension
20454  if (!isset($style['module_width'])) {
20455  $style['module_width'] = 1; // width of a single module in points
20456  }
20457  if (!isset($style['module_height'])) {
20458  $style['module_height'] = 1; // height of a single module in points
20459  }
20460  if ($x === '') {
20461  $x = $this->x;
20462  }
20463  if ($y === '') {
20464  $y = $this->y;
20465  }
20466  // check page for no-write regions and adapt page margins if necessary
20467  list($x, $y) = $this->checkPageRegions($h, $x, $y);
20468  // number of barcode columns and rows
20469  $rows = $arrcode['num_rows'];
20470  $cols = $arrcode['num_cols'];
20471  // module width and height
20472  $mw = $style['module_width'];
20473  $mh = $style['module_height'];
20474  if (($mw == 0) OR ($mh == 0)) {
20475  $this->Error('Error in 2D barcode string');
20476  }
20477  // get max dimensions
20478  if ($this->rtl) {
20479  $maxw = $x - $this->lMargin;
20480  } else {
20481  $maxw = $this->w - $this->rMargin - $x;
20482  }
20483  $maxh = ($this->h - $this->tMargin - $this->bMargin);
20484  $ratioHW = ((($rows * $mh) + $hpad) / (($cols * $mw) + $vpad));
20485  $ratioWH = ((($cols * $mw) + $vpad) / (($rows * $mh) + $hpad));
20486  if (!$distort) {
20487  if (($maxw * $ratioHW) > $maxh) {
20488  $maxw = $maxh * $ratioWH;
20489  }
20490  if (($maxh * $ratioWH) > $maxw) {
20491  $maxh = $maxw * $ratioHW;
20492  }
20493  }
20494  // set maximum dimesions
20495  if ($w > $maxw) {
20496  $w = $maxw;
20497  }
20498  if ($h > $maxh) {
20499  $h = $maxh;
20500  }
20501  // set dimensions
20502  if ((($w === '') OR ($w <= 0)) AND (($h === '') OR ($h <= 0))) {
20503  $w = ($cols + $hpad) * ($mw / $this->k);
20504  $h = ($rows + $vpad) * ($mh / $this->k);
20505  } elseif (($w === '') OR ($w <= 0)) {
20506  $w = $h * $ratioWH;
20507  } elseif (($h === '') OR ($h <= 0)) {
20508  $h = $w * $ratioHW;
20509  }
20510  // barcode size (excluding padding)
20511  $bw = ($w * $cols) / ($cols + $hpad);
20512  $bh = ($h * $rows) / ($rows + $vpad);
20513  // dimension of single barcode cell unit
20514  $cw = $bw / $cols;
20515  $ch = $bh / $rows;
20516  if (!$distort) {
20517  if (($cw / $ch) > ($mw / $mh)) {
20518  // correct horizontal distortion
20519  $cw = $ch * $mw / $mh;
20520  $bw = $cw * $cols;
20521  $style['hpadding'] = ($w - $bw) / (2 * $cw);
20522  } else {
20523  // correct vertical distortion
20524  $ch = $cw * $mh / $mw;
20525  $bh = $ch * $rows;
20526  $style['vpadding'] = ($h - $bh) / (2 * $ch);
20527  }
20528  }
20529  // fit the barcode on available space
20530  list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false);
20531  // set alignment
20532  $this->img_rb_y = $y + $h;
20533  // set alignment
20534  if ($this->rtl) {
20535  if ($style['position'] == 'L') {
20536  $xpos = $this->lMargin;
20537  } elseif ($style['position'] == 'C') {
20538  $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
20539  } elseif ($style['position'] == 'R') {
20540  $xpos = $this->w - $this->rMargin - $w;
20541  } else {
20542  $xpos = $x - $w;
20543  }
20544  $this->img_rb_x = $xpos;
20545  } else {
20546  if ($style['position'] == 'L') {
20547  $xpos = $this->lMargin;
20548  } elseif ($style['position'] == 'C') {
20549  $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
20550  } elseif ($style['position'] == 'R') {
20551  $xpos = $this->w - $this->rMargin - $w;
20552  } else {
20553  $xpos = $x;
20554  }
20555  $this->img_rb_x = $xpos + $w;
20556  }
20557  $xstart = $xpos + ($style['hpadding'] * $cw);
20558  $ystart = $y + ($style['vpadding'] * $ch);
20559  // barcode is always printed in LTR direction
20560  $tempRTL = $this->rtl;
20561  $this->rtl = false;
20562  // print background color
20563  if ($style['bgcolor']) {
20564  $this->Rect($xpos, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
20565  } elseif ($style['border']) {
20566  $this->Rect($xpos, $y, $w, $h, 'D');
20567  }
20568  // set foreground color
20569  $this->SetDrawColorArray($style['fgcolor']);
20570  // print barcode cells
20571  // for each row
20572  for ($r = 0; $r < $rows; ++$r) {
20573  $xr = $xstart;
20574  // for each column
20575  for ($c = 0; $c < $cols; ++$c) {
20576  if ($arrcode['bcode'][$r][$c] == 1) {
20577  // draw a single barcode cell
20578  $this->Rect($xr, $ystart, $cw, $ch, 'F', array(), $style['fgcolor']);
20579  }
20580  $xr += $cw;
20581  }
20582  $ystart += $ch;
20583  }
20584  // restore original direction
20585  $this->rtl = $tempRTL;
20586  // restore previous settings
20587  $this->setGraphicVars($gvars);
20588  // set pointer to align the next text/objects
20589  switch($align) {
20590  case 'T':{
20591  $this->y = $y;
20592  $this->x = $this->img_rb_x;
20593  break;
20594  }
20595  case 'M':{
20596  $this->y = $y + round($h/2);
20597  $this->x = $this->img_rb_x;
20598  break;
20599  }
20600  case 'B':{
20601  $this->y = $this->img_rb_y;
20602  $this->x = $this->img_rb_x;
20603  break;
20604  }
20605  case 'N':{
20606  $this->SetY($this->img_rb_y);
20607  break;
20608  }
20609  default:{
20610  break;
20611  }
20612  }
20613  $this->endlinex = $this->img_rb_x;
20614  }
20615 
20635  public function getMargins() {
20636  $ret = array(
20637  'left' => $this->lMargin,
20638  'right' => $this->rMargin,
20639  'top' => $this->tMargin,
20640  'bottom' => $this->bMargin,
20641  'header' => $this->header_margin,
20642  'footer' => $this->footer_margin,
20643  'cell' => $this->cell_padding,
20644  'padding_left' => $this->cell_padding['L'],
20645  'padding_top' => $this->cell_padding['T'],
20646  'padding_right' => $this->cell_padding['R'],
20647  'padding_bottom' => $this->cell_padding['B']
20648  );
20649  return $ret;
20650  }
20651 
20662  public function getOriginalMargins() {
20663  $ret = array(
20664  'left' => $this->original_lMargin,
20665  'right' => $this->original_rMargin
20666  );
20667  return $ret;
20668  }
20669 
20676  public function getFontSize() {
20677  return $this->FontSize;
20678  }
20679 
20686  public function getFontSizePt() {
20687  return $this->FontSizePt;
20688  }
20689 
20696  public function getFontFamily() {
20697  return $this->FontFamily;
20698  }
20699 
20706  public function getFontStyle() {
20707  return $this->FontStyle;
20708  }
20709 
20722  public function fixHTMLCode($html, $default_css='', $tagvs='', $tidy_options='') {
20723  // configure parameters for HTML Tidy
20724  if ($tidy_options === '') {
20725  $tidy_options = array (
20726  'clean' => 1,
20727  'drop-empty-paras' => 0,
20728  'drop-proprietary-attributes' => 1,
20729  'fix-backslash' => 1,
20730  'hide-comments' => 1,
20731  'join-styles' => 1,
20732  'lower-literals' => 1,
20733  'merge-divs' => 1,
20734  'merge-spans' => 1,
20735  'output-xhtml' => 1,
20736  'word-2000' => 1,
20737  'wrap' => 0,
20738  'output-bom' => 0,
20739  //'char-encoding' => 'utf8',
20740  //'input-encoding' => 'utf8',
20741  //'output-encoding' => 'utf8'
20742  );
20743  }
20744  // clean up the HTML code
20745  $tidy = tidy_parse_string($html, $tidy_options);
20746  // fix the HTML
20747  $tidy->cleanRepair();
20748  // get the CSS part
20749  $tidy_head = tidy_get_head($tidy);
20750  $css = $tidy_head->value;
20751  $css = preg_replace('/<style([^>]+)>/ims', '<style>', $css);
20752  $css = preg_replace('/<\/style>(.*)<style>/ims', "\n", $css);
20753  $css = str_replace('/*<![CDATA[*/', '', $css);
20754  $css = str_replace('/*]]>*/', '', $css);
20755  preg_match('/<style>(.*)<\/style>/ims', $css, $matches);
20756  if (isset($matches[1])) {
20757  $css = strtolower($matches[1]);
20758  } else {
20759  $css = '';
20760  }
20761  // include default css
20762  $css = '<style>'.$default_css.$css.'</style>';
20763  // get the body part
20764  $tidy_body = tidy_get_body($tidy);
20765  $html = $tidy_body->value;
20766  // fix some self-closing tags
20767  $html = str_replace('<br>', '<br />', $html);
20768  // remove some empty tag blocks
20769  $html = preg_replace('/<div([^>]*)><\/div>/', '', $html);
20770  $html = preg_replace('/<p([^>]*)><\/p>/', '', $html);
20771  if ($tagvs !== '') {
20772  // set vertical space for some XHTML tags
20773  $this->setHtmlVSpace($tagvs);
20774  }
20775  // return the cleaned XHTML code + CSS
20776  return $css.$html;
20777  }
20778 
20787  protected function extractCSSproperties($cssdata) {
20788  if (empty($cssdata)) {
20789  return array();
20790  }
20791  // remove comments
20792  $cssdata = preg_replace('/\/\*[^\*]*\*\//', '', $cssdata);
20793  // remove newlines and multiple spaces
20794  $cssdata = preg_replace('/[\s]+/', ' ', $cssdata);
20795  // remove some spaces
20796  $cssdata = preg_replace('/[\s]*([;:\{\}]{1})[\s]*/', '\\1', $cssdata);
20797  // remove empty blocks
20798  $cssdata = preg_replace('/([^\}\{]+)\{\}/', '', $cssdata);
20799  // replace media type parenthesis
20800  $cssdata = preg_replace('/@media[\s]+([^\{]*)\{/i', '@media \\1§', $cssdata);
20801  $cssdata = preg_replace('/\}\}/si', '}§', $cssdata);
20802  // trim string
20803  $cssdata = trim($cssdata);
20804  // find media blocks (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
20805  $cssblocks = array();
20806  $matches = array();
20807  if (preg_match_all('/@media[\s]+([^\§]*)§([^§]*)§/i', $cssdata, $matches) > 0) {
20808  foreach ($matches[1] as $key => $type) {
20809  $cssblocks[$type] = $matches[2][$key];
20810  }
20811  // remove media blocks
20812  $cssdata = preg_replace('/@media[\s]+([^\§]*)§([^§]*)§/i', '', $cssdata);
20813  }
20814  // keep 'all' and 'print' media, other media types are discarded
20815  if (isset($cssblocks['all']) AND !empty($cssblocks['all'])) {
20816  $cssdata .= $cssblocks['all'];
20817  }
20818  if (isset($cssblocks['print']) AND !empty($cssblocks['print'])) {
20819  $cssdata .= $cssblocks['print'];
20820  }
20821  // reset css blocks array
20822  $cssblocks = array();
20823  $matches = array();
20824  // explode css data string into array
20825  if (substr($cssdata, -1) == '}') {
20826  // remove last parethesis
20827  $cssdata = substr($cssdata, 0, -1);
20828  }
20829  $matches = explode('}', $cssdata);
20830  foreach ($matches as $key => $block) {
20831  // index 0 contains the CSS selector, index 1 contains CSS properties
20832  $cssblocks[$key] = explode('{', $block);
20833  if (!isset($cssblocks[$key][1])) {
20834  // remove empty definitions
20835  unset($cssblocks[$key]);
20836  }
20837  }
20838  // split groups of selectors (comma-separated list of selectors)
20839  foreach ($cssblocks as $key => $block) {
20840  if (strpos($block[0], ',') > 0) {
20841  $selectors = explode(',', $block[0]);
20842  foreach ($selectors as $sel) {
20843  $cssblocks[] = array(0 => trim($sel), 1 => $block[1]);
20844  }
20845  unset($cssblocks[$key]);
20846  }
20847  }
20848  // covert array to selector => properties
20849  $cssdata = array();
20850  foreach ($cssblocks as $block) {
20851  $selector = $block[0];
20852  // calculate selector's specificity
20853  $matches = array();
20854  $a = 0; // the declaration is not from is a 'style' attribute
20855  $b = intval(preg_match_all('/[\#]/', $selector, $matches)); // number of ID attributes
20856  $c = intval(preg_match_all('/[\[\.]/', $selector, $matches)); // number of other attributes
20857  $c += intval(preg_match_all('/[\:]link|visited|hover|active|focus|target|lang|enabled|disabled|checked|indeterminate|root|nth|first|last|only|empty|contains|not/i', $selector, $matches)); // number of pseudo-classes
20858  $d = intval(preg_match_all('/[>\+\~\s]{1}[a-zA-Z0-9]+/', ' '.$selector, $matches)); // number of element names
20859  $d += intval(preg_match_all('/[\:][\:]/', $selector, $matches)); // number of pseudo-elements
20860  $specificity = $a.$b.$c.$d;
20861  // add specificity to the beginning of the selector
20862  $cssdata[$specificity.' '.$selector] = $block[1];
20863  }
20864  // sort selectors alphabetically to account for specificity
20865  ksort($cssdata, SORT_STRING);
20866  // return array
20867  return $cssdata;
20868  }
20869 
20879  protected function isValidCSSSelectorForTag($dom, $key, $selector) {
20880  $valid = false; // value to be returned
20881  $tag = $dom[$key]['value'];
20882  $class = array();
20883  if (isset($dom[$key]['attribute']['class']) AND !empty($dom[$key]['attribute']['class'])) {
20884  $class = explode(' ', strtolower($dom[$key]['attribute']['class']));
20885  }
20886  $id = '';
20887  if (isset($dom[$key]['attribute']['id']) AND !empty($dom[$key]['attribute']['id'])) {
20888  $id = strtolower($dom[$key]['attribute']['id']);
20889  }
20890  $selector = preg_replace('/([>\+\~\s]{1})([\.]{1})([^>\+\~\s]*)/si', '\\1*.\\3', $selector);
20891  $matches = array();
20892  if (preg_match_all('/([>\+\~\s]{1})([a-zA-Z0-9\*]+)([^>\+\~\s]*)/si', $selector, $matches, PREG_PATTERN_ORDER | PREG_OFFSET_CAPTURE) > 0) {
20893  $parentop = array_pop($matches[1]);
20894  $operator = $parentop[0];
20895  $offset = $parentop[1];
20896  $lasttag = array_pop($matches[2]);
20897  $lasttag = strtolower(trim($lasttag[0]));
20898  if (($lasttag == '*') OR ($lasttag == $tag)) {
20899  // the last element on selector is our tag or 'any tag'
20900  $attrib = array_pop($matches[3]);
20901  $attrib = strtolower(trim($attrib[0]));
20902  if (!empty($attrib)) {
20903  // check if matches class, id, attribute, pseudo-class or pseudo-element
20904  switch ($attrib{0}) {
20905  case '.': { // class
20906  if (in_array(substr($attrib, 1), $class)) {
20907  $valid = true;
20908  }
20909  break;
20910  }
20911  case '#': { // ID
20912  if (substr($attrib, 1) == $id) {
20913  $valid = true;
20914  }
20915  break;
20916  }
20917  case '[': { // attribute
20918  $attrmatch = array();
20919  if (preg_match('/\[([a-zA-Z0-9]*)[\s]*([\~\^\$\*\|\=]*)[\s]*["]?([^"\]]*)["]?\]/i', $attrib, $attrmatch) > 0) {
20920  $att = strtolower($attrmatch[1]);
20921  $val = $attrmatch[3];
20922  if (isset($dom[$key]['attribute'][$att])) {
20923  switch ($attrmatch[2]) {
20924  case '=': {
20925  if ($dom[$key]['attribute'][$att] == $val) {
20926  $valid = true;
20927  }
20928  break;
20929  }
20930  case '~=': {
20931  if (in_array($val, explode(' ', $dom[$key]['attribute'][$att]))) {
20932  $valid = true;
20933  }
20934  break;
20935  }
20936  case '^=': {
20937  if ($val == substr($dom[$key]['attribute'][$att], 0, strlen($val))) {
20938  $valid = true;
20939  }
20940  break;
20941  }
20942  case '$=': {
20943  if ($val == substr($dom[$key]['attribute'][$att], -strlen($val))) {
20944  $valid = true;
20945  }
20946  break;
20947  }
20948  case '*=': {
20949  if (strpos($dom[$key]['attribute'][$att], $val) !== false) {
20950  $valid = true;
20951  }
20952  break;
20953  }
20954  case '|=': {
20955  if ($dom[$key]['attribute'][$att] == $val) {
20956  $valid = true;
20957  } elseif (preg_match('/'.$val.'[\-]{1}/i', $dom[$key]['attribute'][$att]) > 0) {
20958  $valid = true;
20959  }
20960  break;
20961  }
20962  default: {
20963  $valid = true;
20964  }
20965  }
20966  }
20967  }
20968  break;
20969  }
20970  case ':': { // pseudo-class or pseudo-element
20971  if ($attrib{1} == ':') { // pseudo-element
20972  // pseudo-elements are not supported!
20973  // (::first-line, ::first-letter, ::before, ::after)
20974  } else { // pseudo-class
20975  // pseudo-classes are not supported!
20976  // (:root, :nth-child(n), :nth-last-child(n), :nth-of-type(n), :nth-last-of-type(n), :first-child, :last-child, :first-of-type, :last-of-type, :only-child, :only-of-type, :empty, :link, :visited, :active, :hover, :focus, :target, :lang(fr), :enabled, :disabled, :checked)
20977  }
20978  break;
20979  }
20980  } // end of switch
20981  } else {
20982  $valid = true;
20983  }
20984  if ($valid AND ($offset > 0)) {
20985  $valid = false;
20986  // check remaining selector part
20987  $selector = substr($selector, 0, $offset);
20988  switch ($operator) {
20989  case ' ': { // descendant of an element
20990  while ($dom[$key]['parent'] > 0) {
20991  if ($this->isValidCSSSelectorForTag($dom, $dom[$key]['parent'], $selector)) {
20992  $valid = true;
20993  break;
20994  } else {
20995  $key = $dom[$key]['parent'];
20996  }
20997  }
20998  break;
20999  }
21000  case '>': { // child of an element
21001  $valid = $this->isValidCSSSelectorForTag($dom, $dom[$key]['parent'], $selector);
21002  break;
21003  }
21004  case '+': { // immediately preceded by an element
21005  for ($i = ($key - 1); $i > $dom[$key]['parent']; --$i) {
21006  if ($dom[$i]['tag'] AND $dom[$i]['opening']) {
21007  $valid = $this->isValidCSSSelectorForTag($dom, $i, $selector);
21008  break;
21009  }
21010  }
21011  break;
21012  }
21013  case '~': { // preceded by an element
21014  for ($i = ($key - 1); $i > $dom[$key]['parent']; --$i) {
21015  if ($dom[$i]['tag'] AND $dom[$i]['opening']) {
21016  if ($this->isValidCSSSelectorForTag($dom, $i, $selector)) {
21017  break;
21018  }
21019  }
21020  }
21021  break;
21022  }
21023  }
21024  }
21025  }
21026  }
21027  return $valid;
21028  }
21029 
21039  protected function getCSSdataArray($dom, $key, $css) {
21040  $cssarray = array(); // style to be returned
21041  // get parent CSS selectors
21042  $selectors = array();
21043  if (isset($dom[($dom[$key]['parent'])]['csssel'])) {
21044  $selectors = $dom[($dom[$key]['parent'])]['csssel'];
21045  }
21046  // get all styles that apply
21047  foreach($css as $selector => $style) {
21048  $pos = strpos($selector, ' ');
21049  // get specificity
21050  $specificity = substr($selector, 0, $pos);
21051  // remove specificity
21052  $selector = substr($selector, $pos);
21053  // check if this selector apply to current tag
21054  if ($this->isValidCSSSelectorForTag($dom, $key, $selector)) {
21055  if (!in_array($selector, $selectors)) {
21056  // add style if not already added on parent selector
21057  $cssarray[] = array('k' => $selector, 's' => $specificity, 'c' => $style);
21058  $selectors[] = $selector;
21059  }
21060  }
21061  }
21062  if (isset($dom[$key]['attribute']['style'])) {
21063  // attach inline style (latest properties have high priority)
21064  $cssarray[] = array('k' => '', 's' => '1000', 'c' => $dom[$key]['attribute']['style']);
21065  }
21066  // order the css array to account for specificity
21067  $cssordered = array();
21068  foreach ($cssarray as $key => $val) {
21069  $skey = sprintf('%04d', $key);
21070  $cssordered[$val['s'].'_'.$skey] = $val;
21071  }
21072  // sort selectors alphabetically to account for specificity
21073  ksort($cssordered, SORT_STRING);
21074  return array($selectors, $cssordered);
21075  }
21076 
21084  protected function getTagStyleFromCSSarray($css) {
21085  $tagstyle = ''; // value to be returned
21086  foreach ($css as $style) {
21087  // split single css commands
21088  $csscmds = explode(';', $style['c']);
21089  foreach ($csscmds as $cmd) {
21090  if (!empty($cmd)) {
21091  $pos = strpos($cmd, ':');
21092  if ($pos !== false) {
21093  $cmd = substr($cmd, 0, ($pos + 1));
21094  if (strpos($tagstyle, $cmd) !== false) {
21095  // remove duplicate commands (last commands have high priority)
21096  $tagstyle = preg_replace('/'.$cmd.'[^;]+/i', '', $tagstyle);
21097  }
21098  }
21099  }
21100  }
21101  $tagstyle .= ';'.$style['c'];
21102  }
21103  // remove multiple semicolons
21104  $tagstyle = preg_replace('/[;]+/', ';', $tagstyle);
21105  return $tagstyle;
21106  }
21107 
21115  protected function getCSSBorderWidth($width) {
21116  if ($width == 'thin') {
21117  $width = (2 / $this->k);
21118  } elseif ($width == 'medium') {
21119  $width = (4 / $this->k);
21120  } elseif ($width == 'thick') {
21121  $width = (6 / $this->k);
21122  } else {
21123  $width = $this->getHTMLUnitToUnits($width, 1, 'px', false);
21124  }
21125  return $width;
21126  }
21127 
21135  protected function getCSSBorderDashStyle($style) {
21136  switch (strtolower($style)) {
21137  case 'none':
21138  case 'hidden': {
21139  $dash = -1;
21140  break;
21141  }
21142  case 'dotted': {
21143  $dash = 1;
21144  break;
21145  }
21146  case 'dashed': {
21147  $dash = 3;
21148  break;
21149  }
21150  case 'double':
21151  case 'groove':
21152  case 'ridge':
21153  case 'inset':
21154  case 'outset':
21155  case 'solid':
21156  default: {
21157  $dash = 0;
21158  break;
21159  }
21160  }
21161  return $dash;
21162  }
21163 
21171  protected function getCSSBorderStyle($cssborder) {
21172  $bprop = preg_split('/[\s]+/', trim($cssborder));
21173  $border = array(); // value to be returned
21174  switch (count($bprop)) {
21175  case 3: {
21176  $width = $bprop[0];
21177  $style = $bprop[1];
21178  $color = $bprop[2];
21179  break;
21180  }
21181  case 2: {
21182  $width = 'medium';
21183  $style = $bprop[0];
21184  $color = $bprop[1];
21185  break;
21186  }
21187  case 1: {
21188  $width = 'medium';
21189  $style = $bprop[0];
21190  $color = 'black';
21191  break;
21192  }
21193  default: {
21194  $width = 'medium';
21195  $style = 'solid';
21196  $color = 'black';
21197  break;
21198  }
21199  }
21200  if ($style == 'none') {
21201  return array();
21202  }
21203  $border['cap'] = 'square';
21204  $border['join'] = 'miter';
21205  $border['dash'] = $this->getCSSBorderDashStyle($style);
21206  if ($border['dash'] < 0) {
21207  return array();
21208  }
21209  $border['width'] = $this->getCSSBorderWidth($width);
21210  $border['color'] = $this->convertHTMLColorToDec($color);
21211  return $border;
21212  }
21213 
21222  public function getCSSPadding($csspadding, $width=0) {
21223  $padding = preg_split('/[\s]+/', trim($csspadding));
21224  $cell_padding = array(); // value to be returned
21225  switch (count($padding)) {
21226  case 4: {
21227  $cell_padding['T'] = $padding[0];
21228  $cell_padding['R'] = $padding[1];
21229  $cell_padding['B'] = $padding[2];
21230  $cell_padding['L'] = $padding[3];
21231  break;
21232  }
21233  case 3: {
21234  $cell_padding['T'] = $padding[0];
21235  $cell_padding['R'] = $padding[1];
21236  $cell_padding['B'] = $padding[2];
21237  $cell_padding['L'] = $padding[1];
21238  break;
21239  }
21240  case 2: {
21241  $cell_padding['T'] = $padding[0];
21242  $cell_padding['R'] = $padding[1];
21243  $cell_padding['B'] = $padding[0];
21244  $cell_padding['L'] = $padding[1];
21245  break;
21246  }
21247  case 1: {
21248  $cell_padding['T'] = $padding[0];
21249  $cell_padding['R'] = $padding[0];
21250  $cell_padding['B'] = $padding[0];
21251  $cell_padding['L'] = $padding[0];
21252  break;
21253  }
21254  default: {
21255  return $this->cell_padding;
21256  }
21257  }
21258  if ($width == 0) {
21259  $width = $this->w - $this->lMargin - $this->rMargin;
21260  }
21261  $cell_padding['T'] = $this->getHTMLUnitToUnits($cell_padding['T'], $width, 'px', false);
21262  $cell_padding['R'] = $this->getHTMLUnitToUnits($cell_padding['R'], $width, 'px', false);
21263  $cell_padding['B'] = $this->getHTMLUnitToUnits($cell_padding['B'], $width, 'px', false);
21264  $cell_padding['L'] = $this->getHTMLUnitToUnits($cell_padding['L'], $width, 'px', false);
21265  return $cell_padding;
21266  }
21267 
21276  public function getCSSMargin($cssmargin, $width=0) {
21277  $margin = preg_split('/[\s]+/', trim($cssmargin));
21278  $cell_margin = array(); // value to be returned
21279  switch (count($margin)) {
21280  case 4: {
21281  $cell_margin['T'] = $margin[0];
21282  $cell_margin['R'] = $margin[1];
21283  $cell_margin['B'] = $margin[2];
21284  $cell_margin['L'] = $margin[3];
21285  break;
21286  }
21287  case 3: {
21288  $cell_margin['T'] = $margin[0];
21289  $cell_margin['R'] = $margin[1];
21290  $cell_margin['B'] = $margin[2];
21291  $cell_margin['L'] = $margin[1];
21292  break;
21293  }
21294  case 2: {
21295  $cell_margin['T'] = $margin[0];
21296  $cell_margin['R'] = $margin[1];
21297  $cell_margin['B'] = $margin[0];
21298  $cell_margin['L'] = $margin[1];
21299  break;
21300  }
21301  case 1: {
21302  $cell_margin['T'] = $margin[0];
21303  $cell_margin['R'] = $margin[0];
21304  $cell_margin['B'] = $margin[0];
21305  $cell_margin['L'] = $margin[0];
21306  break;
21307  }
21308  default: {
21309  return $this->cell_margin;
21310  }
21311  }
21312  if ($width == 0) {
21313  $width = $this->w - $this->lMargin - $this->rMargin;
21314  }
21315  $cell_margin['T'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['T']), $width, 'px', false);
21316  $cell_margin['R'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['R']), $width, 'px', false);
21317  $cell_margin['B'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['B']), $width, 'px', false);
21318  $cell_margin['L'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['L']), $width, 'px', false);
21319  return $cell_margin;
21320  }
21321 
21330  public function getCSSBorderMargin($cssbspace, $width=0) {
21331  $space = preg_split('/[\s]+/', trim($cssbspace));
21332  $border_spacing = array(); // value to be returned
21333  switch (count($space)) {
21334  case 2: {
21335  $border_spacing['H'] = $space[0];
21336  $border_spacing['V'] = $space[1];
21337  break;
21338  }
21339  case 1: {
21340  $border_spacing['H'] = $space[0];
21341  $border_spacing['V'] = $space[0];
21342  break;
21343  }
21344  default: {
21345  return array('H' => 0, 'V' => 0);
21346  }
21347  }
21348  if ($width == 0) {
21349  $width = $this->w - $this->lMargin - $this->rMargin;
21350  }
21351  $border_spacing['H'] = $this->getHTMLUnitToUnits($border_spacing['H'], $width, 'px', false);
21352  $border_spacing['V'] = $this->getHTMLUnitToUnits($border_spacing['V'], $width, 'px', false);
21353  return $border_spacing;
21354  }
21355 
21364  protected function getCSSFontSpacing($spacing, $parent=0) {
21365  $val = 0; // value to be returned
21366  $spacing = trim($spacing);
21367  switch ($spacing) {
21368  case 'normal': {
21369  $val = 0;
21370  break;
21371  }
21372  case 'inherit': {
21373  if ($parent == 'normal') {
21374  $val = 0;
21375  } else {
21376  $val = $parent;
21377  }
21378  break;
21379  }
21380  default: {
21381  $val = $this->getHTMLUnitToUnits($spacing, 0, 'px', false);
21382  }
21383  }
21384  return $val;
21385  }
21386 
21395  protected function getCSSFontStretching($stretch, $parent=100) {
21396  $val = 100; // value to be returned
21397  $stretch = trim($stretch);
21398  switch ($stretch) {
21399  case 'ultra-condensed': {
21400  $val = 40;
21401  break;
21402  }
21403  case 'extra-condensed': {
21404  $val = 55;
21405  break;
21406  }
21407  case 'condensed': {
21408  $val = 70;
21409  break;
21410  }
21411  case 'semi-condensed': {
21412  $val = 85;
21413  break;
21414  }
21415  case 'normal': {
21416  $val = 100;
21417  break;
21418  }
21419  case 'semi-expanded': {
21420  $val = 115;
21421  break;
21422  }
21423  case 'expanded': {
21424  $val = 130;
21425  break;
21426  }
21427  case 'extra-expanded': {
21428  $val = 145;
21429  break;
21430  }
21431  case 'ultra-expanded': {
21432  $val = 160;
21433  break;
21434  }
21435  case 'wider': {
21436  $val = $parent + 10;
21437  break;
21438  }
21439  case 'narrower': {
21440  $val = $parent - 10;
21441  break;
21442  }
21443  case 'inherit': {
21444  if ($parent == 'normal') {
21445  $val = 100;
21446  } else {
21447  $val = $parent;
21448  }
21449  break;
21450  }
21451  default: {
21452  $val = $this->getHTMLUnitToUnits($stretch, 100, '%', false);
21453  }
21454  }
21455  return $val;
21456  }
21457 
21465  protected function getHtmlDomArray($html) {
21466  // array of CSS styles ( selector => properties).
21467  $css = array();
21468  // get CSS array defined at previous call
21469  $matches = array();
21470  if (preg_match_all('/<cssarray>([^<]*)<\/cssarray>/isU', $html, $matches) > 0) {
21471  if (isset($matches[1][0])) {
21472  $css = array_merge($css, unserialize($this->unhtmlentities($matches[1][0])));
21473  }
21474  $html = preg_replace('/<cssarray>(.*?)<\/cssarray>/isU', '', $html);
21475  }
21476  // extract external CSS files
21477  $matches = array();
21478  if (preg_match_all('/<link([^>]*)>/isU', $html, $matches) > 0) {
21479  foreach ($matches[1] as $key => $link) {
21480  $type = array();
21481  if (preg_match('/type[\s]*=[\s]*"text\/css"/', $link, $type)) {
21482  $type = array();
21483  preg_match('/media[\s]*=[\s]*"([^"]*)"/', $link, $type);
21484  // get 'all' and 'print' media, other media types are discarded
21485  // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
21486  if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
21487  $type = array();
21488  if (preg_match('/href[\s]*=[\s]*"([^"]*)"/', $link, $type) > 0) {
21489  // read CSS data file
21490  $cssdata = file_get_contents(trim($type[1]));
21491  $css = array_merge($css, $this->extractCSSproperties($cssdata));
21492  }
21493  }
21494  }
21495  }
21496  }
21497  // extract style tags
21498  $matches = array();
21499  if (preg_match_all('/<style([^>]*)>([^<]*)<\/style>/isU', $html, $matches) > 0) {
21500  foreach ($matches[1] as $key => $media) {
21501  $type = array();
21502  preg_match('/media[\s]*=[\s]*"([^"]*)"/', $media, $type);
21503  // get 'all' and 'print' media, other media types are discarded
21504  // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
21505  if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
21506  $cssdata = $matches[2][$key];
21507  $css = array_merge($css, $this->extractCSSproperties($cssdata));
21508  }
21509  }
21510  }
21511  // create a special tag to contain the CSS array (used for table content)
21512  $csstagarray = '<cssarray>'.htmlentities(serialize($css)).'</cssarray>';
21513  // remove head and style blocks
21514  $html = preg_replace('/<head([^>]*)>(.*?)<\/head>/siU', '', $html);
21515  $html = preg_replace('/<style([^>]*)>([^<]*)<\/style>/isU', '', $html);
21516  // define block tags
21517  $blocktags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table','tr','td');
21518  // define self-closing tags
21519  $selfclosingtags = array('area','base','basefont','br','hr','input','img','link','meta');
21520  // remove all unsupported tags (the line below lists all supported tags)
21521  $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>');
21522  //replace some blank characters
21523  $html = preg_replace('/<pre/', '<xre', $html); // preserve pre tag
21524  $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);
21525  $html = preg_replace('@(\r\n|\r)@', "\n", $html);
21526  $repTable = array("\t" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\");
21527  $html = strtr($html, $repTable);
21528  $offset = 0;
21529  while (($offset < strlen($html)) AND ($pos = strpos($html, '</pre>', $offset)) !== false) {
21530  $html_a = substr($html, 0, $offset);
21531  $html_b = substr($html, $offset, ($pos - $offset + 6));
21532  while (preg_match("'<xre([^>]*)>(.*?)\n(.*?)</pre>'si", $html_b)) {
21533  // preserve newlines on <pre> tag
21534  $html_b = preg_replace("'<xre([^>]*)>(.*?)\n(.*?)</pre>'si", "<xre\\1>\\2<br />\\3</pre>", $html_b);
21535  }
21536  while (preg_match("'<xre([^>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], $html_b)) {
21537  // preserve spaces on <pre> tag
21538  $html_b = preg_replace("'<xre([^>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], "<xre\\1>\\2&nbsp;\\3</pre>", $html_b);
21539  }
21540  $html = $html_a.$html_b.substr($html, $pos + 6);
21541  $offset = strlen($html_a.$html_b);
21542  }
21543  $offset = 0;
21544  while (($offset < strlen($html)) AND ($pos = strpos($html, '</textarea>', $offset)) !== false) {
21545  $html_a = substr($html, 0, $offset);
21546  $html_b = substr($html, $offset, ($pos - $offset + 11));
21547  while (preg_match("'<textarea([^>]*)>(.*?)\n(.*?)</textarea>'si", $html_b)) {
21548  // preserve newlines on <textarea> tag
21549  $html_b = preg_replace("'<textarea([^>]*)>(.*?)\n(.*?)</textarea>'si", "<textarea\\1>\\2<TBR>\\3</textarea>", $html_b);
21550  $html_b = preg_replace("'<textarea([^>]*)>(.*?)[\"](.*?)</textarea>'si", "<textarea\\1>\\2''\\3</textarea>", $html_b);
21551  }
21552  $html = $html_a.$html_b.substr($html, $pos + 11);
21553  $offset = strlen($html_a.$html_b);
21554  }
21555  $html = preg_replace('/([\s]*)<option/si', '<option', $html);
21556  $html = preg_replace('/<\/option>([\s]*)/si', '</option>', $html);
21557  $offset = 0;
21558  while (($offset < strlen($html)) AND ($pos = strpos($html, '</option>', $offset)) !== false) {
21559  $html_a = substr($html, 0, $offset);
21560  $html_b = substr($html, $offset, ($pos - $offset + 9));
21561  while (preg_match("'<option([^>]*)>(.*?)</option>'si", $html_b)) {
21562  $html_b = preg_replace("'<option([\s]+)value=\"([^\"]*)\"([^>]*)>(.*?)</option>'si", "\\2#!TaB!#\\4#!NwL!#", $html_b);
21563  $html_b = preg_replace("'<option([^>]*)>(.*?)</option>'si", "\\2#!NwL!#", $html_b);
21564  }
21565  $html = $html_a.$html_b.substr($html, $pos + 9);
21566  $offset = strlen($html_a.$html_b);
21567  }
21568  if (preg_match("'</select'si", $html)) {
21569  $html = preg_replace("'<select([^>]*)>'si", "<select\\1 opt=\"", $html);
21570  $html = preg_replace("'#!NwL!#</select>'si", "\" />", $html);
21571  }
21572  $html = str_replace("\n", ' ', $html);
21573  // restore textarea newlines
21574  $html = str_replace('<TBR>', "\n", $html);
21575  // remove extra spaces from code
21576  $html = preg_replace('/[\s]+<\/(table|tr|ul|ol|dl)>/', '</\\1>', $html);
21577  $html = preg_replace('/'.$this->re_space['p'].'+<\/(td|th|li|dt|dd)>/'.$this->re_space['m'], '</\\1>', $html);
21578  $html = preg_replace('/[\s]+<(tr|td|th|li|dt|dd)/', '<\\1', $html);
21579  $html = preg_replace('/'.$this->re_space['p'].'+<(ul|ol|dl|br)/'.$this->re_space['m'], '<\\1', $html);
21580  $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);
21581  $html = preg_replace('/<\/(td|th)>/', '<marker style="font-size:0"/></\\1>', $html);
21582  $html = preg_replace('/<\/table>([\s]*)<marker style="font-size:0"\/>/', '</table>', $html);
21583  $html = preg_replace('/'.$this->re_space['p'].'+<img/'.$this->re_space['m'], chr(32).'<img', $html);
21584  $html = preg_replace('/<img([^>]*)>[\s]+([^<])/xi', '<img\\1>&nbsp;\\2', $html);
21585  $html = preg_replace('/<img([^>]*)>/xi', '<img\\1><span><marker style="font-size:0"/></span>', $html);
21586  $html = preg_replace('/<xre/', '<pre', $html); // restore pre tag
21587  $html = preg_replace('/<textarea([^>]*)>([^<]*)<\/textarea>/xi', '<textarea\\1 value="\\2" />', $html);
21588  $html = preg_replace('/<li([^>]*)><\/li>/', '<li\\1>&nbsp;</li>', $html);
21589  $html = preg_replace('/<li([^>]*)>'.$this->re_space['p'].'*<img/'.$this->re_space['m'], '<li\\1><font size="1">&nbsp;</font><img', $html);
21590  $html = preg_replace('/<([^>\/]*)>[\s]/', '<\\1>&nbsp;', $html); // preserve some spaces
21591  $html = preg_replace('/[\s]<\/([^>]*)>/', '&nbsp;</\\1>', $html); // preserve some spaces
21592  $html = preg_replace('/<su([bp])/', '<zws/><su\\1', $html); // fix sub/sup alignment
21593  $html = preg_replace('/<\/su([bp])>/', '</su\\1><zws/>', $html); // fix sub/sup alignment
21594  $html = preg_replace('/'.$this->re_space['p'].'+/'.$this->re_space['m'], chr(32), $html); // replace multiple spaces with a single space
21595  // trim string
21596  $html = $this->stringTrim($html);
21597  // fix first image tag alignment
21598  $html = preg_replace('/^<img/', '<span style="font-size:0"><br /></span> <img', $html, 1);
21599  // pattern for generic tag
21600  $tagpattern = '/(<[^>]+>)/';
21601  // explodes the string
21602  $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
21603  // count elements
21604  $maxel = count($a);
21605  $elkey = 0;
21606  $key = 0;
21607  // create an array of elements
21608  $dom = array();
21609  $dom[$key] = array();
21610  // set inheritable properties fot the first void element
21611  // 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
21612  $dom[$key]['tag'] = false;
21613  $dom[$key]['block'] = false;
21614  $dom[$key]['value'] = '';
21615  $dom[$key]['parent'] = 0;
21616  $dom[$key]['hide'] = false;
21617  $dom[$key]['fontname'] = $this->FontFamily;
21618  $dom[$key]['fontstyle'] = $this->FontStyle;
21619  $dom[$key]['fontsize'] = $this->FontSizePt;
21620  $dom[$key]['font-stretch'] = $this->font_stretching;
21621  $dom[$key]['letter-spacing'] = $this->font_spacing;
21622  $dom[$key]['stroke'] = $this->textstrokewidth;
21623  $dom[$key]['fill'] = (($this->textrendermode % 2) == 0);
21624  $dom[$key]['clip'] = ($this->textrendermode > 3);
21625  $dom[$key]['line-height'] = $this->cell_height_ratio;
21626  $dom[$key]['bgcolor'] = false;
21627  $dom[$key]['fgcolor'] = $this->fgcolor; // color
21628  $dom[$key]['strokecolor'] = $this->strokecolor;
21629  $dom[$key]['align'] = '';
21630  $dom[$key]['listtype'] = '';
21631  $dom[$key]['text-indent'] = 0;
21632  $dom[$key]['border'] = array();
21633  $dom[$key]['dir'] = $this->rtl?'rtl':'ltr';
21634  $thead = false; // true when we are inside the THEAD tag
21635  ++$key;
21636  $level = array();
21637  array_push($level, 0); // root
21638  while ($elkey < $maxel) {
21639  $dom[$key] = array();
21640  $element = $a[$elkey];
21641  $dom[$key]['elkey'] = $elkey;
21642  if (preg_match($tagpattern, $element)) {
21643  // html tag
21644  $element = substr($element, 1, -1);
21645  // get tag name
21646  preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag);
21647  $tagname = strtolower($tag[1]);
21648  // check if we are inside a table header
21649  if ($tagname == 'thead') {
21650  if ($element{0} == '/') {
21651  $thead = false;
21652  } else {
21653  $thead = true;
21654  }
21655  ++$elkey;
21656  continue;
21657  }
21658  $dom[$key]['tag'] = true;
21659  $dom[$key]['value'] = $tagname;
21660  if (in_array($dom[$key]['value'], $blocktags)) {
21661  $dom[$key]['block'] = true;
21662  } else {
21663  $dom[$key]['block'] = false;
21664  }
21665  if ($element{0} == '/') {
21666  // *** closing html tag
21667  $dom[$key]['opening'] = false;
21668  $dom[$key]['parent'] = end($level);
21669  array_pop($level);
21670  $dom[$key]['hide'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['hide'];
21671  $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname'];
21672  $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle'];
21673  $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize'];
21674  $dom[$key]['font-stretch'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['font-stretch'];
21675  $dom[$key]['letter-spacing'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['letter-spacing'];
21676  $dom[$key]['stroke'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['stroke'];
21677  $dom[$key]['fill'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fill'];
21678  $dom[$key]['clip'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['clip'];
21679  $dom[$key]['line-height'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['line-height'];
21680  $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor'];
21681  $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor'];
21682  $dom[$key]['strokecolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['strokecolor'];
21683  $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align'];
21684  $dom[$key]['dir'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['dir'];
21685  if (isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'])) {
21686  $dom[$key]['listtype'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'];
21687  }
21688  // set the number of columns in table tag
21689  if (($dom[$key]['value'] == 'tr') AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) {
21690  $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols'];
21691  }
21692  if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
21693  $dom[($dom[$key]['parent'])]['content'] = $csstagarray;
21694  for ($i = ($dom[$key]['parent'] + 1); $i < $key; ++$i) {
21695  $dom[($dom[$key]['parent'])]['content'] .= $a[$dom[$i]['elkey']];
21696  }
21697  $key = $i;
21698  // mark nested tables
21699  $dom[($dom[$key]['parent'])]['content'] = str_replace('<table', '<table nested="true"', $dom[($dom[$key]['parent'])]['content']);
21700  // remove thead sections from nested tables
21701  $dom[($dom[$key]['parent'])]['content'] = str_replace('<thead>', '', $dom[($dom[$key]['parent'])]['content']);
21702  $dom[($dom[$key]['parent'])]['content'] = str_replace('</thead>', '', $dom[($dom[$key]['parent'])]['content']);
21703  }
21704  // store header rows on a new table
21705  if (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['thead'] === true)) {
21706  if ($this->empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) {
21707  $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $csstagarray.$a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']];
21708  }
21709  for ($i = $dom[$key]['parent']; $i <= $key; ++$i) {
21710  $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']];
21711  }
21712  if (!isset($dom[($dom[$key]['parent'])]['attribute'])) {
21713  $dom[($dom[$key]['parent'])]['attribute'] = array();
21714  }
21715  // header elements must be always contained in a single page
21716  $dom[($dom[$key]['parent'])]['attribute']['nobr'] = 'true';
21717  }
21718  if (($dom[$key]['value'] == 'table') AND (!$this->empty_string($dom[($dom[$key]['parent'])]['thead']))) {
21719  // remove the nobr attributes from the table header
21720  $dom[($dom[$key]['parent'])]['thead'] = str_replace(' nobr="true"', '', $dom[($dom[$key]['parent'])]['thead']);
21721  $dom[($dom[$key]['parent'])]['thead'] .= '</tablehead>';
21722  }
21723  } else {
21724  // *** opening or self-closing html tag
21725  $dom[$key]['opening'] = true;
21726  $dom[$key]['parent'] = end($level);
21727  if ((substr($element, -1, 1) == '/') OR (in_array($dom[$key]['value'], $selfclosingtags))) {
21728  // self-closing tag
21729  $dom[$key]['self'] = true;
21730  } else {
21731  // opening tag
21732  array_push($level, $key);
21733  $dom[$key]['self'] = false;
21734  }
21735  // copy some values from parent
21736  $parentkey = 0;
21737  if ($key > 0) {
21738  $parentkey = $dom[$key]['parent'];
21739  $dom[$key]['hide'] = $dom[$parentkey]['hide'];
21740  $dom[$key]['fontname'] = $dom[$parentkey]['fontname'];
21741  $dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle'];
21742  $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'];
21743  $dom[$key]['font-stretch'] = $dom[$parentkey]['font-stretch'];
21744  $dom[$key]['letter-spacing'] = $dom[$parentkey]['letter-spacing'];
21745  $dom[$key]['stroke'] = $dom[$parentkey]['stroke'];
21746  $dom[$key]['fill'] = $dom[$parentkey]['fill'];
21747  $dom[$key]['clip'] = $dom[$parentkey]['clip'];
21748  $dom[$key]['line-height'] = $dom[$parentkey]['line-height'];
21749  $dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor'];
21750  $dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor'];
21751  $dom[$key]['strokecolor'] = $dom[$parentkey]['strokecolor'];
21752  $dom[$key]['align'] = $dom[$parentkey]['align'];
21753  $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
21754  $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
21755  $dom[$key]['border'] = array();
21756  $dom[$key]['dir'] = $dom[$parentkey]['dir'];
21757  }
21758  // get attributes
21759  preg_match_all('/([^=\s]*)[\s]*=[\s]*"([^"]*)"/', $element, $attr_array, PREG_PATTERN_ORDER);
21760  $dom[$key]['attribute'] = array(); // reset attribute array
21761  while (list($id, $name) = each($attr_array[1])) {
21762  $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id];
21763  }
21764  if (!empty($css)) {
21765  // merge CSS style to current style
21766  list($dom[$key]['csssel'], $dom[$key]['cssdata']) = $this->getCSSdataArray($dom, $key, $css);
21767  $dom[$key]['attribute']['style'] = $this->getTagStyleFromCSSarray($dom[$key]['cssdata']);
21768  }
21769  // split style attributes
21770  if (isset($dom[$key]['attribute']['style']) AND !empty($dom[$key]['attribute']['style'])) {
21771  // get style attributes
21772  preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
21773  $dom[$key]['style'] = array(); // reset style attribute array
21774  while (list($id, $name) = each($style_array[1])) {
21775  // in case of duplicate attribute the last replace the previous
21776  $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]);
21777  }
21778  // --- get some style attributes ---
21779  // text direction
21780  if (isset($dom[$key]['style']['direction'])) {
21781  $dom[$key]['dir'] = $dom[$key]['style']['direction'];
21782  }
21783  // display
21784  if (isset($dom[$key]['style']['display'])) {
21785  $dom[$key]['hide'] = (trim(strtolower($dom[$key]['style']['display'])) == 'none');
21786  }
21787  // font family
21788  if (isset($dom[$key]['style']['font-family'])) {
21789  $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['style']['font-family']);
21790  }
21791  // list-style-type
21792  if (isset($dom[$key]['style']['list-style-type'])) {
21793  $dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type']));
21794  if ($dom[$key]['listtype'] == 'inherit') {
21795  $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
21796  }
21797  }
21798  // text-indent
21799  if (isset($dom[$key]['style']['text-indent'])) {
21800  $dom[$key]['text-indent'] = $this->getHTMLUnitToUnits($dom[$key]['style']['text-indent']);
21801  if ($dom[$key]['text-indent'] == 'inherit') {
21802  $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
21803  }
21804  }
21805  // font size
21806  if (isset($dom[$key]['style']['font-size'])) {
21807  $fsize = trim($dom[$key]['style']['font-size']);
21808  switch ($fsize) {
21809  // absolute-size
21810  case 'xx-small': {
21811  $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 4;
21812  break;
21813  }
21814  case 'x-small': {
21815  $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 3;
21816  break;
21817  }
21818  case 'small': {
21819  $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 2;
21820  break;
21821  }
21822  case 'medium': {
21823  $dom[$key]['fontsize'] = $dom[0]['fontsize'];
21824  break;
21825  }
21826  case 'large': {
21827  $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 2;
21828  break;
21829  }
21830  case 'x-large': {
21831  $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 4;
21832  break;
21833  }
21834  case 'xx-large': {
21835  $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 6;
21836  break;
21837  }
21838  // relative-size
21839  case 'smaller': {
21840  $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] - 3;
21841  break;
21842  }
21843  case 'larger': {
21844  $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] + 3;
21845  break;
21846  }
21847  default: {
21848  $dom[$key]['fontsize'] = $this->getHTMLUnitToUnits($fsize, $dom[$parentkey]['fontsize'], 'pt', true);
21849  }
21850  }
21851  }
21852  // font-stretch
21853  if (isset($dom[$key]['style']['font-stretch'])) {
21854  $dom[$key]['font-stretch'] = $this->getCSSFontStretching($dom[$key]['style']['font-stretch'], $dom[$parentkey]['font-stretch']);
21855  }
21856  // letter-spacing
21857  if (isset($dom[$key]['style']['letter-spacing'])) {
21858  $dom[$key]['letter-spacing'] = $this->getCSSFontSpacing($dom[$key]['style']['letter-spacing'], $dom[$parentkey]['letter-spacing']);
21859  }
21860  // line-height
21861  if (isset($dom[$key]['style']['line-height'])) {
21862  $lineheight = trim($dom[$key]['style']['line-height']);
21863  switch ($lineheight) {
21864  // A normal line height. This is default
21865  case 'normal': {
21866  $dom[$key]['line-height'] = $dom[0]['line-height'];
21867  break;
21868  }
21869  default: {
21870  if (is_numeric($lineheight)) {
21871  $lineheight = $lineheight * 100;
21872  }
21873  $dom[$key]['line-height'] = $this->getHTMLUnitToUnits($lineheight, 1, '%', true);
21874  }
21875  }
21876  }
21877  // font style
21878  if (isset($dom[$key]['style']['font-weight'])) {
21879  if (strtolower($dom[$key]['style']['font-weight']{0}) == 'n') {
21880  if (strpos($dom[$key]['fontstyle'], 'B') !== false) {
21881  $dom[$key]['fontstyle'] = str_replace('B', '', $dom[$key]['fontstyle']);
21882  }
21883  } elseif (strtolower($dom[$key]['style']['font-weight']{0}) == 'b') {
21884  $dom[$key]['fontstyle'] .= 'B';
21885  }
21886  }
21887  if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style']{0}) == 'i')) {
21888  $dom[$key]['fontstyle'] .= 'I';
21889  }
21890  // font color
21891  if (isset($dom[$key]['style']['color']) AND (!$this->empty_string($dom[$key]['style']['color']))) {
21892  $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['color']);
21893  } elseif ($dom[$key]['value'] == 'a') {
21894  $dom[$key]['fgcolor'] = $this->htmlLinkColorArray;
21895  }
21896  // background color
21897  if (isset($dom[$key]['style']['background-color']) AND (!$this->empty_string($dom[$key]['style']['background-color']))) {
21898  $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['background-color']);
21899  }
21900  // text-decoration
21901  if (isset($dom[$key]['style']['text-decoration'])) {
21902  $decors = explode(' ', strtolower($dom[$key]['style']['text-decoration']));
21903  foreach ($decors as $dec) {
21904  $dec = trim($dec);
21905  if (!$this->empty_string($dec)) {
21906  if ($dec{0} == 'u') {
21907  // underline
21908  $dom[$key]['fontstyle'] .= 'U';
21909  } elseif ($dec{0} == 'l') {
21910  // line-trough
21911  $dom[$key]['fontstyle'] .= 'D';
21912  } elseif ($dec{0} == 'o') {
21913  // overline
21914  $dom[$key]['fontstyle'] .= 'O';
21915  }
21916  }
21917  }
21918  } elseif ($dom[$key]['value'] == 'a') {
21919  $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle;
21920  }
21921  // check for width attribute
21922  if (isset($dom[$key]['style']['width'])) {
21923  $dom[$key]['width'] = $dom[$key]['style']['width'];
21924  }
21925  // check for height attribute
21926  if (isset($dom[$key]['style']['height'])) {
21927  $dom[$key]['height'] = $dom[$key]['style']['height'];
21928  }
21929  // check for text alignment
21930  if (isset($dom[$key]['style']['text-align'])) {
21931  $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align']{0});
21932  }
21933  // check for CSS border properties
21934  if (isset($dom[$key]['style']['border'])) {
21935  $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border']);
21936  if (!empty($borderstyle)) {
21937  $dom[$key]['border']['LTRB'] = $borderstyle;
21938  }
21939  }
21940  if (isset($dom[$key]['style']['border-color'])) {
21941  $brd_colors = preg_split('/[\s]+/', trim($dom[$key]['style']['border-color']));
21942  if (isset($brd_colors[3])) {
21943  $dom[$key]['border']['L']['color'] = $this->convertHTMLColorToDec($brd_colors[3]);
21944  }
21945  if (isset($brd_colors[1])) {
21946  $dom[$key]['border']['R']['color'] = $this->convertHTMLColorToDec($brd_colors[1]);
21947  }
21948  if (isset($brd_colors[0])) {
21949  $dom[$key]['border']['T']['color'] = $this->convertHTMLColorToDec($brd_colors[0]);
21950  }
21951  if (isset($brd_colors[2])) {
21952  $dom[$key]['border']['B']['color'] = $this->convertHTMLColorToDec($brd_colors[2]);
21953  }
21954  }
21955  if (isset($dom[$key]['style']['border-width'])) {
21956  $brd_widths = preg_split('/[\s]+/', trim($dom[$key]['style']['border-width']));
21957  if (isset($brd_widths[3])) {
21958  $dom[$key]['border']['L']['width'] = $this->getCSSBorderWidth($brd_widths[3]);
21959  }
21960  if (isset($brd_widths[1])) {
21961  $dom[$key]['border']['R']['width'] = $this->getCSSBorderWidth($brd_widths[1]);
21962  }
21963  if (isset($brd_widths[0])) {
21964  $dom[$key]['border']['T']['width'] = $this->getCSSBorderWidth($brd_widths[0]);
21965  }
21966  if (isset($brd_widths[2])) {
21967  $dom[$key]['border']['B']['width'] = $this->getCSSBorderWidth($brd_widths[2]);
21968  }
21969  }
21970  if (isset($dom[$key]['style']['border-style'])) {
21971  $brd_styles = preg_split('/[\s]+/', trim($dom[$key]['style']['border-style']));
21972  if (isset($brd_styles[3]) AND ($brd_styles[3]!='none')) {
21973  $dom[$key]['border']['L']['cap'] = 'square';
21974  $dom[$key]['border']['L']['join'] = 'miter';
21975  $dom[$key]['border']['L']['dash'] = $this->getCSSBorderDashStyle($brd_styles[3]);
21976  if ($dom[$key]['border']['L']['dash'] < 0) {
21977  $dom[$key]['border']['L'] = array();
21978  }
21979  }
21980  if (isset($brd_styles[1])) {
21981  $dom[$key]['border']['R']['cap'] = 'square';
21982  $dom[$key]['border']['R']['join'] = 'miter';
21983  $dom[$key]['border']['R']['dash'] = $this->getCSSBorderDashStyle($brd_styles[1]);
21984  if ($dom[$key]['border']['R']['dash'] < 0) {
21985  $dom[$key]['border']['R'] = array();
21986  }
21987  }
21988  if (isset($brd_styles[0])) {
21989  $dom[$key]['border']['T']['cap'] = 'square';
21990  $dom[$key]['border']['T']['join'] = 'miter';
21991  $dom[$key]['border']['T']['dash'] = $this->getCSSBorderDashStyle($brd_styles[0]);
21992  if ($dom[$key]['border']['T']['dash'] < 0) {
21993  $dom[$key]['border']['T'] = array();
21994  }
21995  }
21996  if (isset($brd_styles[2])) {
21997  $dom[$key]['border']['B']['cap'] = 'square';
21998  $dom[$key]['border']['B']['join'] = 'miter';
21999  $dom[$key]['border']['B']['dash'] = $this->getCSSBorderDashStyle($brd_styles[2]);
22000  if ($dom[$key]['border']['B']['dash'] < 0) {
22001  $dom[$key]['border']['B'] = array();
22002  }
22003  }
22004  }
22005  $cellside = array('L' => 'left', 'R' => 'right', 'T' => 'top', 'B' => 'bottom');
22006  foreach ($cellside as $bsk => $bsv) {
22007  if (isset($dom[$key]['style']['border-'.$bsv])) {
22008  $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border-'.$bsv]);
22009  if (!empty($borderstyle)) {
22010  $dom[$key]['border'][$bsk] = $borderstyle;
22011  }
22012  }
22013  if (isset($dom[$key]['style']['border-'.$bsv.'-color'])) {
22014  $dom[$key]['border'][$bsk]['color'] = $this->convertHTMLColorToDec($dom[$key]['style']['border-'.$bsv.'-color']);
22015  }
22016  if (isset($dom[$key]['style']['border-'.$bsv.'-width'])) {
22017  $dom[$key]['border'][$bsk]['width'] = $this->getCSSBorderWidth($dom[$key]['style']['border-'.$bsv.'-width']);
22018  }
22019  if (isset($dom[$key]['style']['border-'.$bsv.'-style'])) {
22020  $dom[$key]['border'][$bsk]['dash'] = $this->getCSSBorderDashStyle($dom[$key]['style']['border-'.$bsv.'-style']);
22021  if ($dom[$key]['border'][$bsk]['dash'] < 0) {
22022  $dom[$key]['border'][$bsk] = array();
22023  }
22024  }
22025  }
22026  // check for CSS padding properties
22027  if (isset($dom[$key]['style']['padding'])) {
22028  $dom[$key]['padding'] = $this->getCSSPadding($dom[$key]['style']['padding']);
22029  } else {
22030  $dom[$key]['padding'] = $this->cell_padding;
22031  }
22032  foreach ($cellside as $psk => $psv) {
22033  if (isset($dom[$key]['style']['padding-'.$psv])) {
22034  $dom[$key]['padding'][$psk] = $this->getHTMLUnitToUnits($dom[$key]['style']['padding-'.$psv], 0, 'px', false);
22035  }
22036  }
22037  // check for CSS margin properties
22038  if (isset($dom[$key]['style']['margin'])) {
22039  $dom[$key]['margin'] = $this->getCSSMargin($dom[$key]['style']['margin']);
22040  } else {
22041  $dom[$key]['margin'] = $this->cell_margin;
22042  }
22043  foreach ($cellside as $psk => $psv) {
22044  if (isset($dom[$key]['style']['margin-'.$psv])) {
22045  $dom[$key]['margin'][$psk] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $dom[$key]['style']['margin-'.$psv]), 0, 'px', false);
22046  }
22047  }
22048  // check for CSS border-spacing properties
22049  if (isset($dom[$key]['style']['border-spacing'])) {
22050  $dom[$key]['border-spacing'] = $this->getCSSBorderMargin($dom[$key]['style']['border-spacing']);
22051  }
22052  // page-break-inside
22053  if (isset($dom[$key]['style']['page-break-inside']) AND ($dom[$key]['style']['page-break-inside'] == 'avoid')) {
22054  $dom[$key]['attribute']['nobr'] = 'true';
22055  }
22056  // page-break-before
22057  if (isset($dom[$key]['style']['page-break-before'])) {
22058  if ($dom[$key]['style']['page-break-before'] == 'always') {
22059  $dom[$key]['attribute']['pagebreak'] = 'true';
22060  } elseif ($dom[$key]['style']['page-break-before'] == 'left') {
22061  $dom[$key]['attribute']['pagebreak'] = 'left';
22062  } elseif ($dom[$key]['style']['page-break-before'] == 'right') {
22063  $dom[$key]['attribute']['pagebreak'] = 'right';
22064  }
22065  }
22066  // page-break-after
22067  if (isset($dom[$key]['style']['page-break-after'])) {
22068  if ($dom[$key]['style']['page-break-after'] == 'always') {
22069  $dom[$key]['attribute']['pagebreakafter'] = 'true';
22070  } elseif ($dom[$key]['style']['page-break-after'] == 'left') {
22071  $dom[$key]['attribute']['pagebreakafter'] = 'left';
22072  } elseif ($dom[$key]['style']['page-break-after'] == 'right') {
22073  $dom[$key]['attribute']['pagebreakafter'] = 'right';
22074  }
22075  }
22076  }
22077  if (isset($dom[$key]['attribute']['display'])) {
22078  $dom[$key]['hide'] = (trim(strtolower($dom[$key]['attribute']['display'])) == 'none');
22079  }
22080  if (isset($dom[$key]['attribute']['border']) AND ($dom[$key]['attribute']['border'] != 0)) {
22081  $borderstyle = $this->getCSSBorderStyle($dom[$key]['attribute']['border'].' solid black');
22082  if (!empty($borderstyle)) {
22083  $dom[$key]['border']['LTRB'] = $borderstyle;
22084  }
22085  }
22086  // check for font tag
22087  if ($dom[$key]['value'] == 'font') {
22088  // font family
22089  if (isset($dom[$key]['attribute']['face'])) {
22090  $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['attribute']['face']);
22091  }
22092  // font size
22093  if (isset($dom[$key]['attribute']['size'])) {
22094  if ($key > 0) {
22095  if ($dom[$key]['attribute']['size']{0} == '+') {
22096  $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1));
22097  } elseif ($dom[$key]['attribute']['size']{0} == '-') {
22098  $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1));
22099  } else {
22100  $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
22101  }
22102  } else {
22103  $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
22104  }
22105  }
22106  }
22107  // force natural alignment for lists
22108  if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl'))
22109  AND (!isset($dom[$key]['align']) OR $this->empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) {
22110  if ($this->rtl) {
22111  $dom[$key]['align'] = 'R';
22112  } else {
22113  $dom[$key]['align'] = 'L';
22114  }
22115  }
22116  if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) {
22117  if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
22118  $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO;
22119  }
22120  }
22121  if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) {
22122  $dom[$key]['fontstyle'] .= 'B';
22123  }
22124  if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) {
22125  $dom[$key]['fontstyle'] .= 'I';
22126  }
22127  if ($dom[$key]['value'] == 'u') {
22128  $dom[$key]['fontstyle'] .= 'U';
22129  }
22130  if (($dom[$key]['value'] == 'del') OR ($dom[$key]['value'] == 's') OR ($dom[$key]['value'] == 'strike')) {
22131  $dom[$key]['fontstyle'] .= 'D';
22132  }
22133  if (!isset($dom[$key]['style']['text-decoration']) AND ($dom[$key]['value'] == 'a')) {
22134  $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle;
22135  }
22136  if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) {
22137  $dom[$key]['fontname'] = $this->default_monospaced_font;
22138  }
22139  if (($dom[$key]['value']{0} == 'h') AND (intval($dom[$key]['value']{1}) > 0) AND (intval($dom[$key]['value']{1}) < 7)) {
22140  // headings h1, h2, h3, h4, h5, h6
22141  if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
22142  $headsize = (4 - intval($dom[$key]['value']{1})) * 2;
22143  $dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize;
22144  }
22145  if (!isset($dom[$key]['style']['font-weight'])) {
22146  $dom[$key]['fontstyle'] .= 'B';
22147  }
22148  }
22149  if (($dom[$key]['value'] == 'table')) {
22150  $dom[$key]['rows'] = 0; // number of rows
22151  $dom[$key]['trids'] = array(); // IDs of TR elements
22152  $dom[$key]['thead'] = ''; // table header rows
22153  }
22154  if (($dom[$key]['value'] == 'tr')) {
22155  $dom[$key]['cols'] = 0;
22156  if ($thead) {
22157  $dom[$key]['thead'] = true;
22158  // rows on thead block are printed as a separate table
22159  } else {
22160  $dom[$key]['thead'] = false;
22161  // store the number of rows on table element
22162  ++$dom[($dom[$key]['parent'])]['rows'];
22163  // store the TR elements IDs on table element
22164  array_push($dom[($dom[$key]['parent'])]['trids'], $key);
22165  }
22166  }
22167  if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) {
22168  if (isset($dom[$key]['attribute']['colspan'])) {
22169  $colspan = intval($dom[$key]['attribute']['colspan']);
22170  } else {
22171  $colspan = 1;
22172  }
22173  $dom[$key]['attribute']['colspan'] = $colspan;
22174  $dom[($dom[$key]['parent'])]['cols'] += $colspan;
22175  }
22176  // text direction
22177  if (isset($dom[$key]['attribute']['dir'])) {
22178  $dom[$key]['dir'] = $dom[$key]['attribute']['dir'];
22179  }
22180  // set foreground color attribute
22181  if (isset($dom[$key]['attribute']['color']) AND (!$this->empty_string($dom[$key]['attribute']['color']))) {
22182  $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['color']);
22183  } elseif (!isset($dom[$key]['style']['color']) AND ($dom[$key]['value'] == 'a')) {
22184  $dom[$key]['fgcolor'] = $this->htmlLinkColorArray;
22185  }
22186  // set background color attribute
22187  if (isset($dom[$key]['attribute']['bgcolor']) AND (!$this->empty_string($dom[$key]['attribute']['bgcolor']))) {
22188  $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['bgcolor']);
22189  }
22190  // set stroke color attribute
22191  if (isset($dom[$key]['attribute']['strokecolor']) AND (!$this->empty_string($dom[$key]['attribute']['strokecolor']))) {
22192  $dom[$key]['strokecolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['strokecolor']);
22193  }
22194  // check for width attribute
22195  if (isset($dom[$key]['attribute']['width'])) {
22196  $dom[$key]['width'] = $dom[$key]['attribute']['width'];
22197  }
22198  // check for height attribute
22199  if (isset($dom[$key]['attribute']['height'])) {
22200  $dom[$key]['height'] = $dom[$key]['attribute']['height'];
22201  }
22202  // check for text alignment
22203  if (isset($dom[$key]['attribute']['align']) AND (!$this->empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) {
22204  $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align']{0});
22205  }
22206  // check for text rendering mode (the following attributes do not exist in HTML)
22207  if (isset($dom[$key]['attribute']['stroke'])) {
22208  // font stroke width
22209  $dom[$key]['stroke'] = $this->getHTMLUnitToUnits($dom[$key]['attribute']['stroke'], $dom[$key]['fontsize'], 'pt', true);
22210  }
22211  if (isset($dom[$key]['attribute']['fill'])) {
22212  // font fill
22213  if ($dom[$key]['attribute']['fill'] == 'true') {
22214  $dom[$key]['fill'] = true;
22215  } else {
22216  $dom[$key]['fill'] = false;
22217  }
22218  }
22219  if (isset($dom[$key]['attribute']['clip'])) {
22220  // clipping mode
22221  if ($dom[$key]['attribute']['clip'] == 'true') {
22222  $dom[$key]['clip'] = true;
22223  } else {
22224  $dom[$key]['clip'] = false;
22225  }
22226  }
22227  } // end opening tag
22228  } else {
22229  // text
22230  $dom[$key]['tag'] = false;
22231  $dom[$key]['block'] = false;
22232  //$element = str_replace('&nbsp;', $this->unichr(160), $element);
22233  $dom[$key]['value'] = stripslashes($this->unhtmlentities($element));
22234  $dom[$key]['parent'] = end($level);
22235  $dom[$key]['dir'] = $dom[$dom[$key]['parent']]['dir'];
22236  }
22237  ++$elkey;
22238  ++$key;
22239  }
22240  return $dom;
22241  }
22242 
22250  protected function getSpaceString() {
22251  $spacestr = chr(32);
22252  if ($this->isUnicodeFont()) {
22253  $spacestr = chr(0).chr(32);
22254  }
22255  return $spacestr;
22256  }
22257 
22277  public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=false, $reseth=true, $align='', $autopadding=true) {
22278  return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true, $autopadding, 0, 'T', false);
22279  }
22280 
22293  public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') {
22294  $gvars = $this->getGraphicVars();
22295  // store current values
22296  $prev_cell_margin = $this->cell_margin;
22297  $prev_cell_padding = $this->cell_padding;
22298  $prevPage = $this->page;
22299  $prevlMargin = $this->lMargin;
22300  $prevrMargin = $this->rMargin;
22301  $curfontname = $this->FontFamily;
22302  $curfontstyle = $this->FontStyle;
22303  $curfontsize = $this->FontSizePt;
22304  $curfontascent = $this->getFontAscent($curfontname, $curfontstyle, $curfontsize);
22305  $curfontdescent = $this->getFontDescent($curfontname, $curfontstyle, $curfontsize);
22306  $curfontstretcing = $this->font_stretching;
22307  $curfonttracking = $this->font_spacing;
22308  $this->newline = true;
22309  $newline = true;
22310  $startlinepage = $this->page;
22311  $minstartliney = $this->y;
22312  $maxbottomliney = 0;
22313  $startlinex = $this->x;
22314  $startliney = $this->y;
22315  $yshift = 0;
22316  $loop = 0;
22317  $curpos = 0;
22318  $this_method_vars = array();
22319  $undo = false;
22320  $fontaligned = false;
22321  $reverse_dir = false; // true when the text direction is reversed
22322  $this->premode = false;
22323  if ($this->inxobj) {
22324  // we are inside an XObject template
22325  $pask = count($this->xobjects[$this->xobjid]['annotations']);
22326  } elseif (isset($this->PageAnnots[$this->page])) {
22327  $pask = count($this->PageAnnots[$this->page]);
22328  } else {
22329  $pask = 0;
22330  }
22331  if ($this->inxobj) {
22332  // we are inside an XObject template
22333  $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']);
22334  } elseif (!$this->InFooter) {
22335  if (isset($this->footerlen[$this->page])) {
22336  $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
22337  } else {
22338  $this->footerpos[$this->page] = $this->pagelen[$this->page];
22339  }
22340  $startlinepos = $this->footerpos[$this->page];
22341  } else {
22342  // we are inside the footer
22343  $startlinepos = $this->pagelen[$this->page];
22344  }
22345  $lalign = $align;
22346  $plalign = $align;
22347  if ($this->rtl) {
22348  $w = $this->x - $this->lMargin;
22349  } else {
22350  $w = $this->w - $this->rMargin - $this->x;
22351  }
22352  $w -= ($this->cell_padding['L'] + $this->cell_padding['R']);
22353  if ($cell) {
22354  if ($this->rtl) {
22355  $this->x -= $this->cell_padding['R'];
22356  $this->lMargin += $this->cell_padding['R'];
22357  } else {
22358  $this->x += $this->cell_padding['L'];
22359  $this->rMargin += $this->cell_padding['L'];
22360  }
22361  }
22362  if ($this->customlistindent >= 0) {
22363  $this->listindent = $this->customlistindent;
22364  } else {
22365  $this->listindent = $this->GetStringWidth('000000');
22366  }
22367  $this->listindentlevel = 0;
22368  // save previous states
22369  $prev_cell_height_ratio = $this->cell_height_ratio;
22370  $prev_listnum = $this->listnum;
22371  $prev_listordered = $this->listordered;
22372  $prev_listcount = $this->listcount;
22373  $prev_lispacer = $this->lispacer;
22374  $this->listnum = 0;
22375  $this->listordered = array();
22376  $this->listcount = array();
22377  $this->lispacer = '';
22378  if (($this->empty_string($this->lasth)) OR ($reseth)) {
22379  // reset row height
22380  $this->resetLastH();
22381  }
22382  $dom = $this->getHtmlDomArray($html);
22383  $maxel = count($dom);
22384  $key = 0;
22385  $hidden_node_key = -1;
22386  while ($key < $maxel) {
22387  if ($dom[$key]['tag']) {
22388  if ($dom[$key]['opening']) {
22389  if (($hidden_node_key <= 0) AND $dom[$key]['hide']) {
22390  // store the node key
22391  $hidden_node_key = $key;
22392  }
22393  } elseif (($hidden_node_key > 0) AND ($dom[$key]['parent'] == $hidden_node_key)) {
22394  // we have reached the closing tag of the hidden node
22395  $hidden_node_key = 0;
22396  }
22397  }
22398  if ($hidden_node_key >= 0) {
22399  // skip this node
22400  ++$key;
22401  if ($hidden_node_key == 0) {
22402  // reset hidden mode
22403  $hidden_node_key = -1;
22404  }
22405  continue;
22406  }
22407  if ($dom[$key]['tag'] AND isset($dom[$key]['attribute']['pagebreak'])) {
22408  // check for pagebreak
22409  if (($dom[$key]['attribute']['pagebreak'] == 'true') OR ($dom[$key]['attribute']['pagebreak'] == 'left') OR ($dom[$key]['attribute']['pagebreak'] == 'right')) {
22410  // add a page (or trig AcceptPageBreak() for multicolumn mode)
22411  $this->checkPageBreak($this->PageBreakTrigger + 1);
22412  $this->htmlvspace = ($this->PageBreakTrigger + 1);
22413  }
22414  if ((($dom[$key]['attribute']['pagebreak'] == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
22415  OR (($dom[$key]['attribute']['pagebreak'] == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
22416  // add a page (or trig AcceptPageBreak() for multicolumn mode)
22417  $this->checkPageBreak($this->PageBreakTrigger + 1);
22418  $this->htmlvspace = ($this->PageBreakTrigger + 1);
22419  }
22420  }
22421  if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND isset($dom[$key]['attribute']['nobr']) AND ($dom[$key]['attribute']['nobr'] == 'true')) {
22422  if (isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) {
22423  $dom[$key]['attribute']['nobr'] = false;
22424  } else {
22425  // store current object
22426  $this->startTransaction();
22427  // save this method vars
22428  $this_method_vars['html'] = $html;
22429  $this_method_vars['ln'] = $ln;
22430  $this_method_vars['fill'] = $fill;
22431  $this_method_vars['reseth'] = $reseth;
22432  $this_method_vars['cell'] = $cell;
22433  $this_method_vars['align'] = $align;
22434  $this_method_vars['gvars'] = $gvars;
22435  $this_method_vars['prevPage'] = $prevPage;
22436  $this_method_vars['prev_cell_margin'] = $prev_cell_margin;
22437  $this_method_vars['prev_cell_padding'] = $prev_cell_padding;
22438  $this_method_vars['prevlMargin'] = $prevlMargin;
22439  $this_method_vars['prevrMargin'] = $prevrMargin;
22440  $this_method_vars['curfontname'] = $curfontname;
22441  $this_method_vars['curfontstyle'] = $curfontstyle;
22442  $this_method_vars['curfontsize'] = $curfontsize;
22443  $this_method_vars['curfontascent'] = $curfontascent;
22444  $this_method_vars['curfontdescent'] = $curfontdescent;
22445  $this_method_vars['curfontstretcing'] = $curfontstretcing;
22446  $this_method_vars['curfonttracking'] = $curfonttracking;
22447  $this_method_vars['minstartliney'] = $minstartliney;
22448  $this_method_vars['maxbottomliney'] = $maxbottomliney;
22449  $this_method_vars['yshift'] = $yshift;
22450  $this_method_vars['startlinepage'] = $startlinepage;
22451  $this_method_vars['startlinepos'] = $startlinepos;
22452  $this_method_vars['startlinex'] = $startlinex;
22453  $this_method_vars['startliney'] = $startliney;
22454  $this_method_vars['newline'] = $newline;
22455  $this_method_vars['loop'] = $loop;
22456  $this_method_vars['curpos'] = $curpos;
22457  $this_method_vars['pask'] = $pask;
22458  $this_method_vars['lalign'] = $lalign;
22459  $this_method_vars['plalign'] = $plalign;
22460  $this_method_vars['w'] = $w;
22461  $this_method_vars['prev_cell_height_ratio'] = $prev_cell_height_ratio;
22462  $this_method_vars['prev_listnum'] = $prev_listnum;
22463  $this_method_vars['prev_listordered'] = $prev_listordered;
22464  $this_method_vars['prev_listcount'] = $prev_listcount;
22465  $this_method_vars['prev_lispacer'] = $prev_lispacer;
22466  $this_method_vars['fontaligned'] = $fontaligned;
22467  $this_method_vars['key'] = $key;
22468  $this_method_vars['dom'] = $dom;
22469  }
22470  }
22471  // print THEAD block
22472  if (($dom[$key]['value'] == 'tr') AND isset($dom[$key]['thead']) AND $dom[$key]['thead']) {
22473  if (isset($dom[$key]['parent']) AND isset($dom[$dom[$key]['parent']]['thead']) AND !$this->empty_string($dom[$dom[$key]['parent']]['thead'])) {
22474  $this->inthead = true;
22475  // print table header (thead)
22476  $this->writeHTML($this->thead, false, false, false, false, '');
22477  // check if we are on a new page or on a new column
22478  if (($this->y < $this->start_transaction_y) OR ($this->checkPageBreak($this->lasth, '', false))) {
22479  // we are on a new page or on a new column and the total object height is less than the available vertical space.
22480  // restore previous object
22481  $this->rollbackTransaction(true);
22482  // restore previous values
22483  foreach ($this_method_vars as $vkey => $vval) {
22484  $$vkey = $vval;
22485  }
22486  // disable table header
22487  $tmp_thead = $this->thead;
22488  $this->thead = '';
22489  // add a page (or trig AcceptPageBreak() for multicolumn mode)
22490  $pre_y = $this->y;
22491  if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) {
22492  // fix for multicolumn mode
22493  $startliney = $this->y;
22494  }
22495  $this->start_transaction_page = $this->page;
22496  $this->start_transaction_y = $this->y;
22497  // restore table header
22498  $this->thead = $tmp_thead;
22499  // fix table border properties
22500  if (isset($dom[$dom[$key]['parent']]['attribute']['cellspacing'])) {
22501  $tmp_cellspacing = $this->getHTMLUnitToUnits($dom[$dom[$key]['parent']]['attribute']['cellspacing'], 1, 'px');
22502  } elseif (isset($dom[$dom[$key]['parent']]['border-spacing'])) {
22503  $tmp_cellspacing = $dom[$dom[$key]['parent']]['border-spacing']['V'];
22504  } else {
22505  $tmp_cellspacing = 0;
22506  }
22507  $dom[$dom[$key]['parent']]['borderposition']['page'] = $this->page;
22508  $dom[$dom[$key]['parent']]['borderposition']['column'] = $this->current_column;
22509  $dom[$dom[$key]['parent']]['borderposition']['y'] = $this->y + $tmp_cellspacing;
22510  $xoffset = ($this->x - $dom[$dom[$key]['parent']]['borderposition']['x']);
22511  $dom[$dom[$key]['parent']]['borderposition']['x'] += $xoffset;
22512  $dom[$dom[$key]['parent']]['borderposition']['xmax'] += $xoffset;
22513  // print table header (thead)
22514  $this->writeHTML($this->thead, false, false, false, false, '');
22515  }
22516  }
22517  // move $key index forward to skip THEAD block
22518  while ( ($key < $maxel) AND (!(
22519  ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'tr') AND (!isset($dom[$key]['thead']) OR !$dom[$key]['thead']))
22520  OR ($dom[$key]['tag'] AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == 'table'))) )) {
22521  ++$key;
22522  }
22523  }
22524  if ($dom[$key]['tag'] OR ($key == 0)) {
22525  if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) {
22526  $dom[$key]['align'] = ($this->rtl) ? 'R' : 'L';
22527  }
22528  // vertically align image in line
22529  if ((!$this->newline) AND ($dom[$key]['value'] == 'img') AND (isset($dom[$key]['height'])) AND ($dom[$key]['height'] > 0)) {
22530  // get image height
22531  $imgh = $this->getHTMLUnitToUnits($dom[$key]['height'], $this->lasth, 'px');
22532  $autolinebreak = false;
22533  if (isset($dom[$key]['width']) AND ($dom[$key]['width'] > 0)) {
22534  $imgw = $this->getHTMLUnitToUnits($dom[$key]['width'], 1, 'px', false);
22535  if (($imgw <= ($this->w - $this->lMargin - $this->rMargin - $this->cell_padding['L'] - $this->cell_padding['R']))
22536  AND ((($this->rtl) AND (($this->x - $imgw) < ($this->lMargin + $this->cell_padding['L'])))
22537  OR ((!$this->rtl) AND (($this->x + $imgw) > ($this->w - $this->rMargin - $this->cell_padding['R']))))) {
22538  // add automatic line break
22539  $autolinebreak = true;
22540  $this->Ln('', $cell);
22541  if ((!$dom[($key-1)]['tag']) AND ($dom[($key-1)]['value'] == ' ')) {
22542  // go back to evaluate this line break
22543  --$key;
22544  }
22545  }
22546  }
22547  if (!$autolinebreak) {
22548  if ($this->inPageBody()) {
22549  $pre_y = $this->y;
22550  // check for page break
22551  if ((!$this->checkPageBreak($imgh)) AND ($this->y < $pre_y)) {
22552  // fix for multicolumn mode
22553  $startliney = $this->y;
22554  }
22555  }
22556  if ($this->page > $startlinepage) {
22557  // fix line splitted over two pages
22558  if (isset($this->footerlen[$startlinepage])) {
22559  $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
22560  }
22561  // line to be moved one page forward
22562  $pagebuff = $this->getPageBuffer($startlinepage);
22563  $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
22564  $tstart = substr($pagebuff, 0, $startlinepos);
22565  $tend = substr($this->getPageBuffer($startlinepage), $curpos);
22566  // remove line from previous page
22567  $this->setPageBuffer($startlinepage, $tstart.''.$tend);
22568  $pagebuff = $this->getPageBuffer($this->page);
22569  $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
22570  $tend = substr($pagebuff, $this->cntmrk[$this->page]);
22571  // add line start to current page
22572  $yshift = ($minstartliney - $this->y);
22573  if ($fontaligned) {
22574  $yshift += ($curfontsize / $this->k);
22575  }
22576  $try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k));
22577  $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
22578  // shift the annotations and links
22579  if (isset($this->PageAnnots[$this->page])) {
22580  $next_pask = count($this->PageAnnots[$this->page]);
22581  } else {
22582  $next_pask = 0;
22583  }
22584  if (isset($this->PageAnnots[$startlinepage])) {
22585  foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
22586  if ($pak >= $pask) {
22587  $this->PageAnnots[$this->page][] = $pac;
22588  unset($this->PageAnnots[$startlinepage][$pak]);
22589  $npak = count($this->PageAnnots[$this->page]) - 1;
22590  $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
22591  }
22592  }
22593  }
22594  $pask = $next_pask;
22595  $startlinepos = $this->cntmrk[$this->page];
22596  $startlinepage = $this->page;
22597  $startliney = $this->y;
22598  $this->newline = false;
22599  }
22600  $this->y += ((($curfontsize * $this->cell_height_ratio / $this->k) + $curfontascent - $curfontdescent) / 2) - $imgh;
22601  $minstartliney = min($this->y, $minstartliney);
22602  $maxbottomliney = ($startliney + ($this->FontSize * $this->cell_height_ratio));
22603  }
22604  } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize']) OR isset($dom[$key]['line-height'])) {
22605  // account for different font size
22606  $pfontname = $curfontname;
22607  $pfontstyle = $curfontstyle;
22608  $pfontsize = $curfontsize;
22609  $fontname = isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname;
22610  $fontstyle = isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle;
22611  $fontsize = isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize;
22612  $fontascent = $this->getFontAscent($fontname, $fontstyle, $fontsize);
22613  $fontdescent = $this->getFontDescent($fontname, $fontstyle, $fontsize);
22614  if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)
22615  OR ($this->cell_height_ratio != $dom[$key]['line-height'])
22616  OR ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li')) ) {
22617  if (($key < ($maxel - 1)) AND (
22618  ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li'))
22619  OR ($this->cell_height_ratio != $dom[$key]['line-height'])
22620  OR (!$this->newline AND is_numeric($fontsize) AND is_numeric($curfontsize) AND ($fontsize >= 0) AND ($curfontsize >= 0) AND ($fontsize != $curfontsize))
22621  )) {
22622  if ($this->page > $startlinepage) {
22623  // fix lines splitted over two pages
22624  if (isset($this->footerlen[$startlinepage])) {
22625  $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
22626  }
22627  // line to be moved one page forward
22628  $pagebuff = $this->getPageBuffer($startlinepage);
22629  $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
22630  $tstart = substr($pagebuff, 0, $startlinepos);
22631  $tend = substr($this->getPageBuffer($startlinepage), $curpos);
22632  // remove line start from previous page
22633  $this->setPageBuffer($startlinepage, $tstart.''.$tend);
22634  $pagebuff = $this->getPageBuffer($this->page);
22635  $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
22636  $tend = substr($pagebuff, $this->cntmrk[$this->page]);
22637  // add line start to current page
22638  $yshift = ($minstartliney - $this->y);
22639  $try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k));
22640  $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
22641  // shift the annotations and links
22642  if (isset($this->PageAnnots[$this->page])) {
22643  $next_pask = count($this->PageAnnots[$this->page]);
22644  } else {
22645  $next_pask = 0;
22646  }
22647  if (isset($this->PageAnnots[$startlinepage])) {
22648  foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
22649  if ($pak >= $pask) {
22650  $this->PageAnnots[$this->page][] = $pac;
22651  unset($this->PageAnnots[$startlinepage][$pak]);
22652  $npak = count($this->PageAnnots[$this->page]) - 1;
22653  $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
22654  }
22655  }
22656  }
22657  $pask = $next_pask;
22658  $startlinepos = $this->cntmrk[$this->page];
22659  $startlinepage = $this->page;
22660  $startliney = $this->y;
22661  }
22662  if (!isset($dom[$key]['line-height'])) {
22663  $dom[$key]['line-height'] = $this->cell_height_ratio;
22664  }
22665  if (!$dom[$key]['block']) {
22666  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']))) {
22667  $this->y += (((($curfontsize * $this->cell_height_ratio) - ($fontsize * $dom[$key]['line-height'])) / $this->k) + $curfontascent - $fontascent - $curfontdescent + $fontdescent) / 2;
22668  }
22669  if (($dom[$key]['value'] != 'sup') AND ($dom[$key]['value'] != 'sub')) {
22670  $current_line_align_data = array($key, $minstartliney, $maxbottomliney);
22671  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)))) {
22672  $minstartliney = min($this->y, $line_align_data[1]);
22673  $maxbottomliney = max(($this->y + (($fontsize * $this->cell_height_ratio) / $this->k)), $line_align_data[2]);
22674  } else {
22675  $minstartliney = min($this->y, $minstartliney);
22676  $maxbottomliney = max(($this->y + (($fontsize * $this->cell_height_ratio) / $this->k)), $maxbottomliney);
22677  }
22678  $line_align_data = $current_line_align_data;
22679  }
22680  }
22681  $this->cell_height_ratio = $dom[$key]['line-height'];
22682  $fontaligned = true;
22683  }
22684  $this->SetFont($fontname, $fontstyle, $fontsize);
22685  // reset row height
22686  $this->resetLastH();
22687  $curfontname = $fontname;
22688  $curfontstyle = $fontstyle;
22689  $curfontsize = $fontsize;
22690  $curfontascent = $fontascent;
22691  $curfontdescent = $fontdescent;
22692  }
22693  }
22694  // set text rendering mode
22695  $textstroke = isset($dom[$key]['stroke']) ? $dom[$key]['stroke'] : $this->textstrokewidth;
22696  $textfill = isset($dom[$key]['fill']) ? $dom[$key]['fill'] : (($this->textrendermode % 2) == 0);
22697  $textclip = isset($dom[$key]['clip']) ? $dom[$key]['clip'] : ($this->textrendermode > 3);
22698  $this->setTextRenderingMode($textstroke, $textfill, $textclip);
22699  if (isset($dom[$key]['font-stretch']) AND ($dom[$key]['font-stretch'] !== false)) {
22700  $this->setFontStretching($dom[$key]['font-stretch']);
22701  }
22702  if (isset($dom[$key]['letter-spacing']) AND ($dom[$key]['letter-spacing'] !== false)) {
22703  $this->setFontSpacing($dom[$key]['letter-spacing']);
22704  }
22705  if (($plalign == 'J') AND $dom[$key]['block']) {
22706  $plalign = '';
22707  }
22708  // get current position on page buffer
22709  $curpos = $this->pagelen[$startlinepage];
22710  if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) {
22711  $this->SetFillColorArray($dom[$key]['bgcolor']);
22712  $wfill = true;
22713  } else {
22714  $wfill = $fill | false;
22715  }
22716  if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) {
22717  $this->SetTextColorArray($dom[$key]['fgcolor']);
22718  }
22719  if (isset($dom[$key]['strokecolor']) AND ($dom[$key]['strokecolor'] !== false)) {
22720  $this->SetDrawColorArray($dom[$key]['strokecolor']);
22721  }
22722  if (isset($dom[$key]['align'])) {
22723  $lalign = $dom[$key]['align'];
22724  }
22725  if ($this->empty_string($lalign)) {
22726  $lalign = $align;
22727  }
22728  }
22729  // align lines
22730  if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) {
22731  $newline = true;
22732  $fontaligned = false;
22733  // we are at the beginning of a new line
22734  if (isset($startlinex)) {
22735  $yshift = ($minstartliney - $startliney);
22736  if (($yshift > 0) OR ($this->page > $startlinepage)) {
22737  $yshift = 0;
22738  }
22739  $t_x = 0;
22740  // the last line must be shifted to be aligned as requested
22741  $linew = abs($this->endlinex - $startlinex);
22742  if ($this->inxobj) {
22743  // we are inside an XObject template
22744  $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos);
22745  if (isset($opentagpos)) {
22746  $midpos = $opentagpos;
22747  } else {
22748  $midpos = 0;
22749  }
22750  if ($midpos > 0) {
22751  $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos));
22752  $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos);
22753  } else {
22754  $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos);
22755  $pend = '';
22756  }
22757  } else {
22758  $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
22759  if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
22760  $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
22761  $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
22762  } elseif (isset($opentagpos)) {
22763  $midpos = $opentagpos;
22764  } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
22765  $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
22766  $midpos = $this->footerpos[$startlinepage];
22767  } else {
22768  $midpos = 0;
22769  }
22770  if ($midpos > 0) {
22771  $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
22772  $pend = substr($this->getPageBuffer($startlinepage), $midpos);
22773  } else {
22774  $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
22775  $pend = '';
22776  }
22777  }
22778  if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) {
22779  // calculate shifting amount
22780  $tw = $w;
22781  if (($plalign == 'J') AND $this->isRTLTextDir() AND ($this->num_columns > 1)) {
22782  $tw += $this->cell_padding['R'];
22783  }
22784  if ($this->lMargin != $prevlMargin) {
22785  $tw += ($prevlMargin - $this->lMargin);
22786  }
22787  if ($this->rMargin != $prevrMargin) {
22788  $tw += ($prevrMargin - $this->rMargin);
22789  }
22790  $one_space_width = $this->GetStringWidth(chr(32));
22791  $no = 0; // number of spaces on a line contained on a single block
22792  if ($this->isRTLTextDir()) { // RTL
22793  // remove left space if exist
22794  $pos1 = $this->revstrpos($pmid, '[(');
22795  if ($pos1 > 0) {
22796  $pos1 = intval($pos1);
22797  if ($this->isUnicodeFont()) {
22798  $pos2 = intval($this->revstrpos($pmid, '[('.chr(0).chr(32)));
22799  $spacelen = 2;
22800  } else {
22801  $pos2 = intval($this->revstrpos($pmid, '[('.chr(32)));
22802  $spacelen = 1;
22803  }
22804  if ($pos1 == $pos2) {
22805  $pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen));
22806  if (substr($pmid, $pos1, 4) == '[()]') {
22807  $linew -= $one_space_width;
22808  } elseif ($pos1 == strpos($pmid, '[(')) {
22809  $no = 1;
22810  }
22811  }
22812  }
22813  } else { // LTR
22814  // remove right space if exist
22815  $pos1 = $this->revstrpos($pmid, ')]');
22816  if ($pos1 > 0) {
22817  $pos1 = intval($pos1);
22818  if ($this->isUnicodeFont()) {
22819  $pos2 = intval($this->revstrpos($pmid, chr(0).chr(32).')]')) + 2;
22820  $spacelen = 2;
22821  } else {
22822  $pos2 = intval($this->revstrpos($pmid, chr(32).')]')) + 1;
22823  $spacelen = 1;
22824  }
22825  if ($pos1 == $pos2) {
22826  $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
22827  $linew -= $one_space_width;
22828  }
22829  }
22830  }
22831  $mdiff = ($tw - $linew);
22832  if ($plalign == 'C') {
22833  if ($this->rtl) {
22834  $t_x = -($mdiff / 2);
22835  } else {
22836  $t_x = ($mdiff / 2);
22837  }
22838  } elseif ($plalign == 'R') {
22839  // right alignment on LTR document
22840  $t_x = $mdiff;
22841  } elseif ($plalign == 'L') {
22842  // left alignment on RTL document
22843  $t_x = -$mdiff;
22844  } elseif (($plalign == 'J') AND ($plalign == $lalign)) {
22845  // Justification
22846  if ($this->isRTLTextDir()) {
22847  // align text on the left
22848  $t_x = -$mdiff;
22849  }
22850  $ns = 0; // number of spaces
22851  $pmidtemp = $pmid;
22852  // escape special characters
22853  $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
22854  $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
22855  // search spaces
22856  if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER)) {
22857  $spacestr = $this->getSpaceString();
22858  $maxkk = count($lnstring[1]) - 1;
22859  for ($kk=0; $kk <= $maxkk; ++$kk) {
22860  // restore special characters
22861  $lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]);
22862  $lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]);
22863  // store number of spaces on the strings
22864  $lnstring[2][$kk] = substr_count($lnstring[1][$kk], $spacestr);
22865  // count total spaces on line
22866  $ns += $lnstring[2][$kk];
22867  $lnstring[3][$kk] = $ns;
22868  }
22869  if ($ns == 0) {
22870  $ns = 1;
22871  }
22872  // calculate additional space to add to each existing space
22873  $spacewidth = ($mdiff / ($ns - $no)) * $this->k;
22874  $spacewidthu = -1000 * ($mdiff + (($ns + $no) * $one_space_width)) / $ns / $this->FontSize;
22875  if ($this->font_spacing != 0) {
22876  // fixed spacing mode
22877  $osw = -1000 * $this->font_spacing / $this->FontSize;
22878  $spacewidthu += $osw;
22879  }
22880  $nsmax = $ns;
22881  $ns = 0;
22882  reset($lnstring);
22883  $offset = 0;
22884  $strcount = 0;
22885  $prev_epsposbeg = 0;
22886  $textpos = 0;
22887  if ($this->isRTLTextDir()) {
22888  $textpos = $this->wPt;
22889  }
22890  global $spacew;
22891  while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE, $offset) == 1) {
22892  // check if we are inside a string section '[( ... )]'
22893  $stroffset = strpos($pmid, '[(', $offset);
22894  if (($stroffset !== false) AND ($stroffset <= $strpiece[2][1])) {
22895  // set offset to the end of string section
22896  $offset = strpos($pmid, ')]', $stroffset);
22897  while (($offset !== false) AND ($pmid[($offset - 1)] == '\\')) {
22898  $offset = strpos($pmid, ')]', ($offset + 1));
22899  }
22900  if ($offset === false) {
22901  $this->Error('HTML Justification: malformed PDF code.');
22902  }
22903  continue;
22904  }
22905  if ($this->isRTLTextDir()) {
22906  $spacew = ($spacewidth * ($nsmax - $ns));
22907  } else {
22908  $spacew = ($spacewidth * $ns);
22909  }
22910  $offset = $strpiece[2][1] + strlen($strpiece[2][0]);
22911  $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, $offset);
22912  $epsposend = strpos($pmid, $this->epsmarker.'Q', $offset) + strlen($this->epsmarker.'Q');
22913  if ((($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend))
22914  OR (($epsposbeg === false) AND ($epsposend > 0) AND ($offset < $epsposend))) {
22915  // shift EPS images
22916  $trx = sprintf('1 0 0 1 %F 0 cm', $spacew);
22917  $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, ($prev_epsposbeg - 6));
22918  $pmid_b = substr($pmid, 0, $epsposbeg);
22919  $pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg));
22920  $pmid_e = substr($pmid, $epsposend);
22921  $pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e;
22922  $offset = $epsposend;
22923  continue;
22924 
22925  }
22926  $prev_epsposbeg = $epsposbeg;
22927  $currentxpos = 0;
22928  // shift blocks of code
22929  switch ($strpiece[2][0]) {
22930  case 'Td':
22931  case 'cm':
22932  case 'm':
22933  case 'l': {
22934  // get current X position
22935  preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
22936  $currentxpos = $xmatches[1];
22937  $textpos = $currentxpos;
22938  if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) {
22939  $ns = $lnstring[3][$strcount];
22940  if ($this->isRTLTextDir()) {
22941  $spacew = ($spacewidth * ($nsmax - $ns));
22942  }
22943  ++$strcount;
22944  }
22945  // justify block
22946  $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x',
22947  create_function('$matches', 'global $spacew;
22948  $newx = sprintf("%F",(floatval($matches[1]) + $spacew));
22949  return "".$newx." ".$matches[2]." x*#!#*x".$matches[3].$matches[4];'), $pmid, 1);
22950  break;
22951  }
22952  case 're': {
22953  // justify block
22954  if (!$this->empty_string($this->lispacer)) {
22955  $this->lispacer = '';
22956  continue;
22957  }
22958  preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $xmatches);
22959  $currentxpos = $xmatches[1];
22960  global $x_diff, $w_diff;
22961  $x_diff = 0;
22962  $w_diff = 0;
22963  if ($this->isRTLTextDir()) { // RTL
22964  if ($currentxpos < $textpos) {
22965  $x_diff = ($spacewidth * ($nsmax - $lnstring[3][$strcount]));
22966  $w_diff = ($spacewidth * $lnstring[2][$strcount]);
22967  } else {
22968  if ($strcount > 0) {
22969  $x_diff = ($spacewidth * ($nsmax - $lnstring[3][($strcount - 1)]));
22970  $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
22971  }
22972  }
22973  } else { // LTR
22974  if ($currentxpos > $textpos) {
22975  if ($strcount > 0) {
22976  $x_diff = ($spacewidth * $lnstring[3][($strcount - 1)]);
22977  }
22978  $w_diff = ($spacewidth * $lnstring[2][$strcount]);
22979  } else {
22980  if ($strcount > 1) {
22981  $x_diff = ($spacewidth * $lnstring[3][($strcount - 2)]);
22982  }
22983  if ($strcount > 0) {
22984  $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
22985  }
22986  }
22987  }
22988  $pmid = preg_replace_callback('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x',
22989  create_function('$matches', 'global $x_diff, $w_diff;
22990  $newx = sprintf("%F",(floatval($matches[1]) + $x_diff));
22991  $neww = sprintf("%F",(floatval($matches[3]) + $w_diff));
22992  return "".$newx." ".$matches[2]." ".$neww." ".$matches[4]." x*#!#*x".$matches[5].$matches[6];'), $pmid, 1);
22993  break;
22994  }
22995  case 'c': {
22996  // get current X position
22997  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);
22998  $currentxpos = $xmatches[1];
22999  // justify block
23000  $pmid = preg_replace_callback('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$xmatches[4].')[\s]('.$xmatches[5].')[\s]('.$strpiece[1][0].')[\s](c)([\s]*)/x',
23001  create_function('$matches', 'global $spacew;
23002  $newx1 = sprintf("%F",(floatval($matches[1]) + $spacew));
23003  $newx2 = sprintf("%F",(floatval($matches[3]) + $spacew));
23004  $newx3 = sprintf("%F",(floatval($matches[5]) + $spacew));
23005  return "".$newx1." ".$matches[2]." ".$newx2." ".$matches[4]." ".$newx3." ".$matches[6]." x*#!#*x".$matches[7].$matches[8];'), $pmid, 1);
23006  break;
23007  }
23008  }
23009  // shift the annotations and links
23010  $cxpos = ($currentxpos / $this->k);
23011  $lmpos = ($this->lMargin + $this->cell_padding['L'] + $this->feps);
23012  if ($this->inxobj) {
23013  // we are inside an XObject template
23014  foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
23015  if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
23016  if ($cxpos > $lmpos) {
23017  $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += ($spacew / $this->k);
23018  $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
23019  } else {
23020  $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
23021  }
23022  break;
23023  }
23024  }
23025  } elseif (isset($this->PageAnnots[$this->page])) {
23026  foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
23027  if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
23028  if ($cxpos > $lmpos) {
23029  $this->PageAnnots[$this->page][$pak]['x'] += ($spacew / $this->k);
23030  $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
23031  } else {
23032  $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
23033  }
23034  break;
23035  }
23036  }
23037  }
23038  } // end of while
23039  // remove markers
23040  $pmid = str_replace('x*#!#*x', '', $pmid);
23041  if ($this->isUnicodeFont()) {
23042  // multibyte characters
23043  $spacew = $spacewidthu;
23044  if ($this->font_stretching != 100) {
23045  // word spacing is affected by stretching
23046  $spacew /= ($this->font_stretching / 100);
23047  }
23048  $pmidtemp = $pmid;
23049  // escape special characters
23050  $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
23051  $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
23052  $pmid = preg_replace_callback("/\[\(([^\)]*)\)\]/x",
23053  create_function('$matches', 'global $spacew;
23054  $matches[1] = str_replace("#!#OP#!#", "(", $matches[1]);
23055  $matches[1] = str_replace("#!#CP#!#", ")", $matches[1]);
23056  return "[(".str_replace(chr(0).chr(32), ") ".sprintf("%F", $spacew)." (", $matches[1]).")]";'), $pmidtemp);
23057  if ($this->inxobj) {
23058  // we are inside an XObject template
23059  $this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\n".$pend;
23060  } else {
23061  $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend);
23062  }
23063  $endlinepos = strlen($pstart."\n".$pmid."\n");
23064  } else {
23065  // non-unicode (single-byte characters)
23066  if ($this->font_stretching != 100) {
23067  // word spacing (Tw) is affected by stretching
23068  $spacewidth /= ($this->font_stretching / 100);
23069  }
23070  $rs = sprintf('%F Tw', $spacewidth);
23071  $pmid = preg_replace("/\[\(/x", $rs.' [(', $pmid);
23072  if ($this->inxobj) {
23073  // we are inside an XObject template
23074  $this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend;
23075  } else {
23076  $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend);
23077  }
23078  $endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n");
23079  }
23080  }
23081  } // end of J
23082  } // end if $startlinex
23083  if (($t_x != 0) OR ($yshift < 0)) {
23084  // shift the line
23085  $trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k), ($yshift * $this->k));
23086  $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
23087  $endlinepos = strlen($pstart);
23088  if ($this->inxobj) {
23089  // we are inside an XObject template
23090  $this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend;
23091  foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
23092  if ($pak >= $pask) {
23093  $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x;
23094  $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift;
23095  }
23096  }
23097  } else {
23098  $this->setPageBuffer($startlinepage, $pstart.$pend);
23099  // shift the annotations and links
23100  if (isset($this->PageAnnots[$this->page])) {
23101  foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
23102  if ($pak >= $pask) {
23103  $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
23104  $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
23105  }
23106  }
23107  }
23108  }
23109  $this->y -= $yshift;
23110  }
23111  }
23112  $pbrk = $this->checkPageBreak($this->lasth);
23113  $this->newline = false;
23114  $startlinex = $this->x;
23115  $startliney = $this->y;
23116  if ($dom[$dom[$key]['parent']]['value'] == 'sup') {
23117  $startliney -= ((0.3 * $this->FontSizePt) / $this->k);
23118  } elseif ($dom[$dom[$key]['parent']]['value'] == 'sub') {
23119  $startliney -= (($this->FontSizePt / 0.7) / $this->k);
23120  } else {
23121  $minstartliney = $startliney;
23122  $maxbottomliney = ($this->y + (($fontsize * $this->cell_height_ratio) / $this->k));
23123  }
23124  $startlinepage = $this->page;
23125  if (isset($endlinepos) AND (!$pbrk)) {
23126  $startlinepos = $endlinepos;
23127  } else {
23128  if ($this->inxobj) {
23129  // we are inside an XObject template
23130  $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']);
23131  } elseif (!$this->InFooter) {
23132  if (isset($this->footerlen[$this->page])) {
23133  $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
23134  } else {
23135  $this->footerpos[$this->page] = $this->pagelen[$this->page];
23136  }
23137  $startlinepos = $this->footerpos[$this->page];
23138  } else {
23139  $startlinepos = $this->pagelen[$this->page];
23140  }
23141  }
23142  unset($endlinepos);
23143  $plalign = $lalign;
23144  if (isset($this->PageAnnots[$this->page])) {
23145  $pask = count($this->PageAnnots[$this->page]);
23146  } else {
23147  $pask = 0;
23148  }
23149  if (!($dom[$key]['tag'] AND !$dom[$key]['opening'] AND ($dom[$key]['value'] == 'table')
23150  AND (isset($this->emptypagemrk[$this->page]))
23151  AND ($this->emptypagemrk[$this->page] == $this->pagelen[$this->page]))) {
23152  $this->SetFont($fontname, $fontstyle, $fontsize);
23153  if ($wfill) {
23154  $this->SetFillColorArray($this->bgcolor);
23155  }
23156  }
23157  } // end newline
23158  if (isset($opentagpos)) {
23159  unset($opentagpos);
23160  }
23161  if ($dom[$key]['tag']) {
23162  if ($dom[$key]['opening']) {
23163  // get text indentation (if any)
23164  if (isset($dom[$key]['text-indent']) AND $dom[$key]['block']) {
23165  $this->textindent = $dom[$key]['text-indent'];
23166  $this->newline = true;
23167  }
23168  // table
23169  if ($dom[$key]['value'] == 'table') {
23170  // available page width
23171  if ($this->rtl) {
23172  $wtmp = $this->x - $this->lMargin;
23173  } else {
23174  $wtmp = $this->w - $this->rMargin - $this->x;
23175  }
23176  // get cell spacing
23177  if (isset($dom[$key]['attribute']['cellspacing'])) {
23178  $clsp = $this->getHTMLUnitToUnits($dom[$key]['attribute']['cellspacing'], 1, 'px');
23179  $cellspacing = array('H' => $clsp, 'V' => $clsp);
23180  } elseif (isset($dom[$key]['border-spacing'])) {
23181  $cellspacing = $dom[$key]['border-spacing'];
23182  } else {
23183  $cellspacing = array('H' => 0, 'V' => 0);
23184  }
23185  // table width
23186  if (isset($dom[$key]['width'])) {
23187  $table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px');
23188  } else {
23189  $table_width = $wtmp;
23190  }
23191  $table_width -= (2 * $cellspacing['H']);
23192  if (!$this->inthead) {
23193  $this->y += $cellspacing['V'];
23194  }
23195  if ($this->rtl) {
23196  $cellspacingx = -$cellspacing['H'];
23197  } else {
23198  $cellspacingx = $cellspacing['H'];
23199  }
23200  // total table width without cellspaces
23201  $table_columns_width = ($table_width - ($cellspacing['H'] * ($dom[$key]['cols'] - 1)));
23202  // minimum column width
23203  $table_min_column_width = ($table_columns_width / $dom[$key]['cols']);
23204  // array of custom column widths
23205  $table_colwidths = array_fill(0, $dom[$key]['cols'], $table_min_column_width);
23206  }
23207  // table row
23208  if ($dom[$key]['value'] == 'tr') {
23209  // reset column counter
23210  $colid = 0;
23211  }
23212  // table cell
23213  if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
23214  $trid = $dom[$key]['parent'];
23215  $table_el = $dom[$trid]['parent'];
23216  if (!isset($dom[$table_el]['cols'])) {
23217  $dom[$table_el]['cols'] = $dom[$trid]['cols'];
23218  }
23219  // store border info
23220  $tdborder = 0;
23221  if (isset($dom[$key]['border']) AND !empty($dom[$key]['border'])) {
23222  $tdborder = $dom[$key]['border'];
23223  }
23224  $colspan = $dom[$key]['attribute']['colspan'];
23225  $old_cell_padding = $this->cell_padding;
23226  if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) {
23227  $crclpd = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px');
23228  $current_cell_padding = array('L' => $crclpd, 'T' => $crclpd, 'R' => $crclpd, 'B' => $crclpd);
23229  } elseif (isset($dom[($dom[$trid]['parent'])]['padding'])) {
23230  $current_cell_padding = $dom[($dom[$trid]['parent'])]['padding'];
23231  } else {
23232  $current_cell_padding = array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0);
23233  }
23234  $this->cell_padding = $current_cell_padding;
23235  if (isset($dom[$key]['height'])) {
23236  // minimum cell height
23237  $cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px');
23238  } else {
23239  $cellh = 0;
23240  }
23241  if (isset($dom[$key]['content'])) {
23242  $cell_content = stripslashes($dom[$key]['content']);
23243  } else {
23244  $cell_content = '&nbsp;';
23245  }
23246  $tagtype = $dom[$key]['value'];
23247  $parentid = $key;
23248  while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) {
23249  // move $key index forward
23250  ++$key;
23251  }
23252  if (!isset($dom[$trid]['startpage'])) {
23253  $dom[$trid]['startpage'] = $this->page;
23254  } else {
23255  $this->setPage($dom[$trid]['startpage']);
23256  }
23257  if (!isset($dom[$trid]['startcolumn'])) {
23258  $dom[$trid]['startcolumn'] = $this->current_column;
23259  } elseif ($this->current_column != $dom[$trid]['startcolumn']) {
23260  $tmpx = $this->x;
23261  $this->selectColumn($dom[$trid]['startcolumn']);
23262  $this->x = $tmpx;
23263  }
23264  if (!isset($dom[$trid]['starty'])) {
23265  $dom[$trid]['starty'] = $this->y;
23266  } else {
23267  $this->y = $dom[$trid]['starty'];
23268  }
23269  if (!isset($dom[$trid]['startx'])) {
23270  $dom[$trid]['startx'] = $this->x;
23271  $this->x += $cellspacingx;
23272  } else {
23273  $this->x += ($cellspacingx / 2);
23274  }
23275  if (isset($dom[$parentid]['attribute']['rowspan'])) {
23276  $rowspan = intval($dom[$parentid]['attribute']['rowspan']);
23277  } else {
23278  $rowspan = 1;
23279  }
23280  // skip row-spanned cells started on the previous rows
23281  if (isset($dom[$table_el]['rowspans'])) {
23282  $rsk = 0;
23283  $rskmax = count($dom[$table_el]['rowspans']);
23284  while ($rsk < $rskmax) {
23285  $trwsp = $dom[$table_el]['rowspans'][$rsk];
23286  $rsstartx = $trwsp['startx'];
23287  $rsendx = $trwsp['endx'];
23288  // account for margin changes
23289  if ($trwsp['startpage'] < $this->page) {
23290  if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$trwsp['startpage']]['orm'])) {
23291  $dl = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$trwsp['startpage']]['orm']);
23292  $rsstartx -= $dl;
23293  $rsendx -= $dl;
23294  } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$trwsp['startpage']]['olm'])) {
23295  $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$trwsp['startpage']]['olm']);
23296  $rsstartx += $dl;
23297  $rsendx += $dl;
23298  }
23299  }
23300  if (($trwsp['rowspan'] > 0)
23301  AND ($rsstartx > ($this->x - $cellspacing['H'] - $current_cell_padding['L'] - $this->feps))
23302  AND ($rsstartx < ($this->x + $cellspacing['H'] + $current_cell_padding['R'] + $this->feps))
23303  AND (($trwsp['starty'] < ($this->y - $this->feps)) OR ($trwsp['startpage'] < $this->page) OR ($trwsp['startcolumn'] < $this->current_column))) {
23304  // set the starting X position of the current cell
23305  $this->x = $rsendx + $cellspacingx;
23306  // increment column indicator
23307  $colid += $trwsp['colspan'];
23308  if (($trwsp['rowspan'] == 1)
23309  AND (isset($dom[$trid]['endy']))
23310  AND (isset($dom[$trid]['endpage']))
23311  AND (isset($dom[$trid]['endcolumn']))
23312  AND ($trwsp['endpage'] == $dom[$trid]['endpage'])
23313  AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
23314  // set ending Y position for row
23315  $dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
23316  $dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy'];
23317  }
23318  $rsk = 0;
23319  } else {
23320  ++$rsk;
23321  }
23322  }
23323  }
23324  if (isset($dom[$parentid]['width'])) {
23325  // user specified width
23326  $cellw = $this->getHTMLUnitToUnits($dom[$parentid]['width'], $table_columns_width, 'px');
23327  $tmpcw = ($cellw / $colspan);
23328  for ($i = 0; $i < $colspan; ++$i) {
23329  $table_colwidths[($colid + $i)] = $tmpcw;
23330  }
23331  } else {
23332  // inherit column width
23333  $cellw = 0;
23334  for ($i = 0; $i < $colspan; ++$i) {
23335  $cellw += $table_colwidths[($colid + $i)];
23336  }
23337  }
23338  $cellw += (($colspan - 1) * $cellspacing['H']);
23339  // increment column indicator
23340  $colid += $colspan;
23341  // add rowspan information to table element
23342  if ($rowspan > 1) {
23343  $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));
23344  }
23345  $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x));
23346  if ($rowspan > 1) {
23347  $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1);
23348  }
23349  // push background colors
23350  if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) {
23351  $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor'];
23352  }
23353  // store border info
23354  if (isset($tdborder) AND !empty($tdborder)) {
23355  $dom[$trid]['cellpos'][($cellid - 1)]['border'] = $tdborder;
23356  }
23357  $prevLastH = $this->lasth;
23358  // store some info for multicolumn mode
23359  if ($this->rtl) {
23360  $this->colxshift['x'] = $this->w - $this->x - $this->rMargin;
23361  } else {
23362  $this->colxshift['x'] = $this->x - $this->lMargin;
23363  }
23364  $this->colxshift['s'] = $cellspacing;
23365  $this->colxshift['p'] = $current_cell_padding;
23366  // ****** write the cell content ******
23367  $this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true, true, 0, 'T', false);
23368  // restore some values
23369  $this->colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
23370  $this->lasth = $prevLastH;
23371  $this->cell_padding = $old_cell_padding;
23372  $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x;
23373  // update the end of row position
23374  if ($rowspan <= 1) {
23375  if (isset($dom[$trid]['endy'])) {
23376  if (($this->page == $dom[$trid]['endpage']) AND ($this->current_column == $dom[$trid]['endcolumn'])) {
23377  $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']);
23378  } elseif (($this->page > $dom[$trid]['endpage']) OR ($this->current_column > $dom[$trid]['endcolumn'])) {
23379  $dom[$trid]['endy'] = $this->y;
23380  }
23381  } else {
23382  $dom[$trid]['endy'] = $this->y;
23383  }
23384  if (isset($dom[$trid]['endpage'])) {
23385  $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']);
23386  } else {
23387  $dom[$trid]['endpage'] = $this->page;
23388  }
23389  if (isset($dom[$trid]['endcolumn'])) {
23390  $dom[$trid]['endcolumn'] = max($this->current_column, $dom[$trid]['endcolumn']);
23391  } else {
23392  $dom[$trid]['endcolumn'] = $this->current_column;
23393  }
23394  } else {
23395  // account for row-spanned cells
23396  $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x;
23397  $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y;
23398  $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page;
23399  $dom[$table_el]['rowspans'][($trsid - 1)]['endcolumn'] = $this->current_column;
23400  }
23401  if (isset($dom[$table_el]['rowspans'])) {
23402  // update endy and endpage on rowspanned cells
23403  foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
23404  if ($trwsp['rowspan'] > 0) {
23405  if (isset($dom[$trid]['endpage'])) {
23406  if (($trwsp['endpage'] == $dom[$trid]['endpage']) AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
23407  $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
23408  } elseif (($trwsp['endpage'] < $dom[$trid]['endpage']) OR ($trwsp['endcolumn'] < $dom[$trid]['endcolumn'])) {
23409  $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy'];
23410  $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage'];
23411  $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[$trid]['endcolumn'];
23412  } else {
23413  $dom[$trid]['endy'] = $this->pagedim[$dom[$trid]['endpage']]['hk'] - $this->pagedim[$dom[$trid]['endpage']]['bm'];
23414  }
23415  }
23416  }
23417  }
23418  }
23419  $this->x += ($cellspacingx / 2);
23420  } else {
23421  // opening tag (or self-closing tag)
23422  if (!isset($opentagpos)) {
23423  if ($this->inxobj) {
23424  // we are inside an XObject template
23425  $opentagpos = strlen($this->xobjects[$this->xobjid]['outdata']);
23426  } elseif (!$this->InFooter) {
23427  if (isset($this->footerlen[$this->page])) {
23428  $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
23429  } else {
23430  $this->footerpos[$this->page] = $this->pagelen[$this->page];
23431  }
23432  $opentagpos = $this->footerpos[$this->page];
23433  }
23434  }
23435  $dom = $this->openHTMLTagHandler($dom, $key, $cell);
23436  }
23437  } else { // closing tag
23438  $prev_numpages = $this->numpages;
23439  $old_bordermrk = $this->bordermrk[$this->page];
23440  $dom = $this->closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney);
23441  if ($this->bordermrk[$this->page] > $old_bordermrk) {
23442  $startlinepos += ($this->bordermrk[$this->page] - $old_bordermrk);
23443  }
23444  if ($prev_numpages > $this->numpages) {
23445  $startlinepage = $this->page;
23446  }
23447  }
23448  } elseif (strlen($dom[$key]['value']) > 0) {
23449  // print list-item
23450  if (!$this->empty_string($this->lispacer) AND ($this->lispacer != '^')) {
23451  $this->SetFont($pfontname, $pfontstyle, $pfontsize);
23452  $this->resetLastH();
23453  $minstartliney = $this->y;
23454  $maxbottomliney = ($startliney + ($this->FontSize * $this->cell_height_ratio));
23455  $this->putHtmlListBullet($this->listnum, $this->lispacer, $pfontsize);
23456  $this->SetFont($curfontname, $curfontstyle, $curfontsize);
23457  $this->resetLastH();
23458  if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) {
23459  $pfontascent = $this->getFontAscent($pfontname, $pfontstyle, $pfontsize);
23460  $pfontdescent = $this->getFontDescent($pfontname, $pfontstyle, $pfontsize);
23461  $this->y += ((($pfontsize - $curfontsize) * $this->cell_height_ratio / $this->k) + $pfontascent - $curfontascent - $pfontdescent + $curfontdescent) / 2;
23462  $minstartliney = min($this->y, $minstartliney);
23463  $maxbottomliney = max(($this->y + (($pfontsize * $this->cell_height_ratio) / $this->k)), $maxbottomliney);
23464  }
23465  }
23466  // text
23467  $this->htmlvspace = 0;
23468  if ((!$this->premode) AND $this->isRTLTextDir()) {
23469  // reverse spaces order
23470  $lsp = ''; // left spaces
23471  $rsp = ''; // right spaces
23472  if (preg_match('/^('.$this->re_space['p'].'+)/'.$this->re_space['m'], $dom[$key]['value'], $matches)) {
23473  $lsp = $matches[1];
23474  }
23475  if (preg_match('/('.$this->re_space['p'].'+)$/'.$this->re_space['m'], $dom[$key]['value'], $matches)) {
23476  $rsp = $matches[1];
23477  }
23478  $dom[$key]['value'] = $rsp.$this->stringTrim($dom[$key]['value']).$lsp;
23479  }
23480  if ($newline) {
23481  if (!$this->premode) {
23482  $prelen = strlen($dom[$key]['value']);
23483  if ($this->isRTLTextDir()) {
23484  // right trim except non-breaking space
23485  $dom[$key]['value'] = $this->stringRightTrim($dom[$key]['value']);
23486  } else {
23487  // left trim except non-breaking space
23488  $dom[$key]['value'] = $this->stringLeftTrim($dom[$key]['value']);
23489  }
23490  $postlen = strlen($dom[$key]['value']);
23491  if (($postlen == 0) AND ($prelen > 0)) {
23492  $dom[$key]['trimmed_space'] = true;
23493  }
23494  }
23495  $newline = false;
23496  $firstblock = true;
23497  } else {
23498  $firstblock = false;
23499  // replace empty multiple spaces string with a single space
23500  $dom[$key]['value'] = preg_replace('/^'.$this->re_space['p'].'+$/'.$this->re_space['m'], chr(32), $dom[$key]['value']);
23501  }
23502  $strrest = '';
23503  if ($this->rtl) {
23504  $this->x -= $this->textindent;
23505  } else {
23506  $this->x += $this->textindent;
23507  }
23508  if (!isset($dom[$key]['trimmed_space']) OR !$dom[$key]['trimmed_space']) {
23509  $strlinelen = $this->GetStringWidth($dom[$key]['value']);
23510  if (!empty($this->HREF) AND (isset($this->HREF['url']))) {
23511  // HTML <a> Link
23512  $hrefcolor = '';
23513  if (isset($dom[($dom[$key]['parent'])]['fgcolor']) AND ($dom[($dom[$key]['parent'])]['fgcolor'] !== false)) {
23514  $hrefcolor = $dom[($dom[$key]['parent'])]['fgcolor'];
23515  }
23516  $hrefstyle = -1;
23517  if (isset($dom[($dom[$key]['parent'])]['fontstyle']) AND ($dom[($dom[$key]['parent'])]['fontstyle'] !== false)) {
23518  $hrefstyle = $dom[($dom[$key]['parent'])]['fontstyle'];
23519  }
23520  $strrest = $this->addHtmlLink($this->HREF['url'], $dom[$key]['value'], $wfill, true, $hrefcolor, $hrefstyle, true);
23521  } else {
23522  $wadj = 0; // space to leave for block continuity
23523  if ($this->rtl) {
23524  $cwa = ($this->x - $this->lMargin);
23525  } else {
23526  $cwa = ($this->w - $this->rMargin - $this->x);
23527  }
23528  if (($strlinelen < $cwa) AND (isset($dom[($key + 1)])) AND ($dom[($key + 1)]['tag']) AND (!$dom[($key + 1)]['block'])) {
23529  // check the next text blocks for continuity
23530  $nkey = ($key + 1);
23531  $write_block = true;
23532  $same_textdir = true;
23533  $tmp_fontname = $this->FontFamily;
23534  $tmp_fontstyle = $this->FontStyle;
23535  $tmp_fontsize = $this->FontSizePt;
23536  while ($write_block AND isset($dom[$nkey])) {
23537  if ($dom[$nkey]['tag']) {
23538  if ($dom[$nkey]['block']) {
23539  // end of block
23540  $write_block = false;
23541  }
23542  $tmp_fontname = isset($dom[$nkey]['fontname']) ? $dom[$nkey]['fontname'] : $this->FontFamily;
23543  $tmp_fontstyle = isset($dom[$nkey]['fontstyle']) ? $dom[$nkey]['fontstyle'] : $this->FontStyle;
23544  $tmp_fontsize = isset($dom[$nkey]['fontsize']) ? $dom[$nkey]['fontsize'] : $this->FontSizePt;
23545  $same_textdir = ($dom[$nkey]['dir'] == $dom[$key]['dir']);
23546  } else {
23547  $nextstr = preg_split('/'.$this->re_space['p'].'+/'.$this->re_space['m'], $dom[$nkey]['value']);
23548  if (isset($nextstr[0]) AND $same_textdir) {
23549  $wadj += $this->GetStringWidth($nextstr[0], $tmp_fontname, $tmp_fontstyle, $tmp_fontsize);
23550  if (isset($nextstr[1])) {
23551  $write_block = false;
23552  }
23553  }
23554  }
23555  ++$nkey;
23556  }
23557  }
23558  if (($wadj > 0) AND (($strlinelen + $wadj) >= $cwa)) {
23559  $wadj = 0;
23560  $nextstr = preg_split('/'.$this->re_space['p'].'/'.$this->re_space['m'], $dom[$key]['value']);
23561  $numblks = count($nextstr);
23562  if ($numblks > 1) {
23563  // try to split on blank spaces
23564  $wadj = ($cwa - $strlinelen + $this->GetStringWidth($nextstr[($numblks - 1)]));
23565  } else {
23566  // set the entire block on new line
23567  $wadj = $this->GetStringWidth($nextstr[0]);
23568  }
23569  }
23570  // check for reversed text direction
23571  if (($wadj > 0) AND (($this->rtl AND ($this->tmprtl === 'L')) OR (!$this->rtl AND ($this->tmprtl === 'R')))) {
23572  // LTR text on RTL direction or RTL text on LTR direction
23573  $reverse_dir = true;
23574  $this->rtl = !$this->rtl;
23575  $revshift = ($strlinelen + $wadj + 0.000001); // add little quantity for rounding problems
23576  if ($this->rtl) {
23577  $this->x += $revshift;
23578  } else {
23579  $this->x -= $revshift;
23580  }
23581  $xws = $this->x;
23582  }
23583  // ****** write only until the end of the line and get the rest ******
23584  $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock, 0, $wadj);
23585  // restore default direction
23586  if ($reverse_dir AND ($wadj == 0)) {
23587  $this->x = $xws;
23588  $this->rtl = !$this->rtl;
23589  $reverse_dir = false;
23590  }
23591  }
23592  }
23593  $this->textindent = 0;
23594  if (strlen($strrest) > 0) {
23595  // store the remaining string on the previous $key position
23596  $this->newline = true;
23597  if ($strrest == $dom[$key]['value']) {
23598  // used to avoid infinite loop
23599  ++$loop;
23600  } else {
23601  $loop = 0;
23602  }
23603  $dom[$key]['value'] = $strrest;
23604  if ($cell) {
23605  if ($this->rtl) {
23606  $this->x -= $this->cell_padding['R'];
23607  } else {
23608  $this->x += $this->cell_padding['L'];
23609  }
23610  }
23611  if ($loop < 3) {
23612  --$key;
23613  }
23614  } else {
23615  $loop = 0;
23616  // add the positive font spacing of the last character (if any)
23617  if ($this->font_spacing > 0) {
23618  if ($this->rtl) {
23619  $this->x -= $this->font_spacing;
23620  } else {
23621  $this->x += $this->font_spacing;
23622  }
23623  }
23624  }
23625  }
23626  ++$key;
23627  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')) {
23628  // check if we are on a new page or on a new column
23629  if ((!$undo) AND (($this->y < $this->start_transaction_y) OR (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['endy'] < $this->start_transaction_y)))) {
23630  // we are on a new page or on a new column and the total object height is less than the available vertical space.
23631  // restore previous object
23632  $this->rollbackTransaction(true);
23633  // restore previous values
23634  foreach ($this_method_vars as $vkey => $vval) {
23635  $$vkey = $vval;
23636  }
23637  // add a page (or trig AcceptPageBreak() for multicolumn mode)
23638  $pre_y = $this->y;
23639  if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) {
23640  $startliney = $this->y;
23641  }
23642  $undo = true; // avoid infinite loop
23643  } else {
23644  $undo = false;
23645  }
23646  }
23647  } // end for each $key
23648  // align the last line
23649  if (isset($startlinex)) {
23650  $yshift = ($minstartliney - $startliney);
23651  if (($yshift > 0) OR ($this->page > $startlinepage)) {
23652  $yshift = 0;
23653  }
23654  $t_x = 0;
23655  // the last line must be shifted to be aligned as requested
23656  $linew = abs($this->endlinex - $startlinex);
23657  if ($this->inxobj) {
23658  // we are inside an XObject template
23659  $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos);
23660  if (isset($opentagpos)) {
23661  $midpos = $opentagpos;
23662  } else {
23663  $midpos = 0;
23664  }
23665  if ($midpos > 0) {
23666  $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos));
23667  $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos);
23668  } else {
23669  $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos);
23670  $pend = '';
23671  }
23672  } else {
23673  $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
23674  if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
23675  $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
23676  $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
23677  } elseif (isset($opentagpos)) {
23678  $midpos = $opentagpos;
23679  } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
23680  $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
23681  $midpos = $this->footerpos[$startlinepage];
23682  } else {
23683  $midpos = 0;
23684  }
23685  if ($midpos > 0) {
23686  $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
23687  $pend = substr($this->getPageBuffer($startlinepage), $midpos);
23688  } else {
23689  $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
23690  $pend = '';
23691  }
23692  }
23693  if ((isset($plalign) AND ((($plalign == 'C') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) {
23694  // calculate shifting amount
23695  $tw = $w;
23696  if ($this->lMargin != $prevlMargin) {
23697  $tw += ($prevlMargin - $this->lMargin);
23698  }
23699  if ($this->rMargin != $prevrMargin) {
23700  $tw += ($prevrMargin - $this->rMargin);
23701  }
23702  $one_space_width = $this->GetStringWidth(chr(32));
23703  $no = 0; // number of spaces on a line contained on a single block
23704  if ($this->isRTLTextDir()) { // RTL
23705  // remove left space if exist
23706  $pos1 = $this->revstrpos($pmid, '[(');
23707  if ($pos1 > 0) {
23708  $pos1 = intval($pos1);
23709  if ($this->isUnicodeFont()) {
23710  $pos2 = intval($this->revstrpos($pmid, '[('.chr(0).chr(32)));
23711  $spacelen = 2;
23712  } else {
23713  $pos2 = intval($this->revstrpos($pmid, '[('.chr(32)));
23714  $spacelen = 1;
23715  }
23716  if ($pos1 == $pos2) {
23717  $pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen));
23718  if (substr($pmid, $pos1, 4) == '[()]') {
23719  $linew -= $one_space_width;
23720  } elseif ($pos1 == strpos($pmid, '[(')) {
23721  $no = 1;
23722  }
23723  }
23724  }
23725  } else { // LTR
23726  // remove right space if exist
23727  $pos1 = $this->revstrpos($pmid, ')]');
23728  if ($pos1 > 0) {
23729  $pos1 = intval($pos1);
23730  if ($this->isUnicodeFont()) {
23731  $pos2 = intval($this->revstrpos($pmid, chr(0).chr(32).')]')) + 2;
23732  $spacelen = 2;
23733  } else {
23734  $pos2 = intval($this->revstrpos($pmid, chr(32).')]')) + 1;
23735  $spacelen = 1;
23736  }
23737  if ($pos1 == $pos2) {
23738  $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
23739  $linew -= $one_space_width;
23740  }
23741  }
23742  }
23743  $mdiff = ($tw - $linew);
23744  if ($plalign == 'C') {
23745  if ($this->rtl) {
23746  $t_x = -($mdiff / 2);
23747  } else {
23748  $t_x = ($mdiff / 2);
23749  }
23750  } elseif ($plalign == 'R') {
23751  // right alignment on LTR document
23752  $t_x = $mdiff;
23753  } elseif ($plalign == 'L') {
23754  // left alignment on RTL document
23755  $t_x = -$mdiff;
23756  }
23757  } // end if startlinex
23758  if (($t_x != 0) OR ($yshift < 0)) {
23759  // shift the line
23760  $trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k), ($yshift * $this->k));
23761  $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
23762  $endlinepos = strlen($pstart);
23763  if ($this->inxobj) {
23764  // we are inside an XObject template
23765  $this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend;
23766  foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
23767  if ($pak >= $pask) {
23768  $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x;
23769  $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift;
23770  }
23771  }
23772  } else {
23773  $this->setPageBuffer($startlinepage, $pstart.$pend);
23774  // shift the annotations and links
23775  if (isset($this->PageAnnots[$this->page])) {
23776  foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
23777  if ($pak >= $pask) {
23778  $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
23779  $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
23780  }
23781  }
23782  }
23783  }
23784  $this->y -= $yshift;
23785  $yshift = 0;
23786  }
23787  }
23788  // restore previous values
23789  $this->setGraphicVars($gvars);
23790  if ($this->num_columns > 1) {
23791  $this->selectColumn();
23792  } elseif ($this->page > $prevPage) {
23793  $this->lMargin = $this->pagedim[$this->page]['olm'];
23794  $this->rMargin = $this->pagedim[$this->page]['orm'];
23795  }
23796  // restore previous list state
23797  $this->cell_height_ratio = $prev_cell_height_ratio;
23798  $this->listnum = $prev_listnum;
23799  $this->listordered = $prev_listordered;
23800  $this->listcount = $prev_listcount;
23801  $this->lispacer = $prev_lispacer;
23802  if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) {
23803  $this->Ln($this->lasth);
23804  if ($this->y < $maxbottomliney) {
23805  $this->y = $maxbottomliney;
23806  }
23807  }
23808  unset($dom);
23809  }
23810 
23819  protected function openHTMLTagHandler($dom, $key, $cell) {
23820  $tag = $dom[$key];
23821  $parent = $dom[($dom[$key]['parent'])];
23822  $firsttag = ($key == 1);
23823  // check for text direction attribute
23824  if (isset($tag['dir'])) {
23825  $this->setTempRTL($tag['dir']);
23826  } else {
23827  $this->tmprtl = false;
23828  }
23829  if ($tag['block']) {
23830  $hbz = 0; // distance from y to line bottom
23831  $hb = 0; // vertical space between block tags
23832  // calculate vertical space for block tags
23833  if (isset($this->tagvspaces[$tag['value']][0]['h']) AND ($this->tagvspaces[$tag['value']][0]['h'] >= 0)) {
23834  $cur_h = $this->tagvspaces[$tag['value']][0]['h'];
23835  } elseif (isset($tag['fontsize'])) {
23836  $cur_h = ($tag['fontsize'] / $this->k) * $this->cell_height_ratio;
23837  } else {
23838  $cur_h = $this->FontSize * $this->cell_height_ratio;
23839  }
23840  if (isset($this->tagvspaces[$tag['value']][0]['n'])) {
23841  $n = $this->tagvspaces[$tag['value']][0]['n'];
23842  } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
23843  $n = 0.6;
23844  } else {
23845  $n = 1;
23846  }
23847  if ((!isset($this->tagvspaces[$tag['value']])) AND (in_array($tag['value'], array('div', 'dt', 'dd', 'li', 'br')))) {
23848  $hb = 0;
23849  } else {
23850  $hb = ($n * $cur_h);
23851  }
23852  if (($this->htmlvspace <= 0) AND ($n > 0)) {
23853  if (isset($parent['fontsize'])) {
23854  $hbz = (($parent['fontsize'] / $this->k) * $this->cell_height_ratio);
23855  } else {
23856  $hbz = $this->FontSize * $this->cell_height_ratio;
23857  }
23858  }
23859  if (isset($dom[($key - 1)]) AND ($dom[($key - 1)]['value'] == 'table')) {
23860  // fix vertical space after table
23861  $hbz = 0;
23862  }
23863  }
23864  // Opening tag
23865  switch($tag['value']) {
23866  case 'table': {
23867  $cp = 0;
23868  $cs = 0;
23869  $dom[$key]['rowspans'] = array();
23870  if (!isset($dom[$key]['attribute']['nested']) OR ($dom[$key]['attribute']['nested'] != 'true')) {
23871  $this->htmlvspace = 0;
23872  // set table header
23873  if (!$this->empty_string($dom[$key]['thead'])) {
23874  // set table header
23875  $this->thead = $dom[$key]['thead'];
23876  if (!isset($this->theadMargins) OR (empty($this->theadMargins))) {
23877  $this->theadMargins = array();
23878  $this->theadMargins['cell_padding'] = $this->cell_padding;
23879  $this->theadMargins['lmargin'] = $this->lMargin;
23880  $this->theadMargins['rmargin'] = $this->rMargin;
23881  $this->theadMargins['page'] = $this->page;
23882  $this->theadMargins['cell'] = $cell;
23883  }
23884  }
23885  }
23886  // store current margins and page
23887  $dom[$key]['old_cell_padding'] = $this->cell_padding;
23888  if (isset($tag['attribute']['cellpadding'])) {
23889  $pad = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px');
23890  $this->SetCellPadding($pad);
23891  } elseif (isset($tag['padding'])) {
23892  $this->cell_padding = $tag['padding'];
23893  }
23894  if (isset($tag['attribute']['cellspacing'])) {
23895  $cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
23896  } elseif (isset($tag['border-spacing'])) {
23897  $cs = $tag['border-spacing']['V'];
23898  }
23899  $prev_y = $this->y;
23900  if ($this->checkPageBreak(((2 * $cp) + (2 * $cs) + $this->lasth), '', false) OR ($this->y < $prev_y)) {
23901  $this->inthead = true;
23902  // add a page (or trig AcceptPageBreak() for multicolumn mode)
23903  $this->checkPageBreak($this->PageBreakTrigger + 1);
23904  }
23905  break;
23906  }
23907  case 'tr': {
23908  // array of columns positions
23909  $dom[$key]['cellpos'] = array();
23910  break;
23911  }
23912  case 'hr': {
23913  if ((isset($tag['height'])) AND ($tag['height'] != '')) {
23914  $hrHeight = $this->getHTMLUnitToUnits($tag['height'], 1, 'px');
23915  } else {
23916  $hrHeight = $this->GetLineWidth();
23917  }
23918  $this->addHTMLVertSpace($hbz, ($hrHeight / 2), $cell, $firsttag);
23919  $x = $this->GetX();
23920  $y = $this->GetY();
23921  $wtmp = $this->w - $this->lMargin - $this->rMargin;
23922  if ($cell) {
23923  $wtmp -= ($this->cell_padding['L'] + $this->cell_padding['R']);
23924  }
23925  if ((isset($tag['width'])) AND ($tag['width'] != '')) {
23926  $hrWidth = $this->getHTMLUnitToUnits($tag['width'], $wtmp, 'px');
23927  } else {
23928  $hrWidth = $wtmp;
23929  }
23930  $prevlinewidth = $this->GetLineWidth();
23931  $this->SetLineWidth($hrHeight);
23932  $this->Line($x, $y, $x + $hrWidth, $y);
23933  $this->SetLineWidth($prevlinewidth);
23934  $this->addHTMLVertSpace(($hrHeight / 2), 0, $cell, !isset($dom[($key + 1)]));
23935  break;
23936  }
23937  case 'a': {
23938  if (array_key_exists('href', $tag['attribute'])) {
23939  $this->HREF['url'] = $tag['attribute']['href'];
23940  }
23941  break;
23942  }
23943  case 'img': {
23944  if (isset($tag['attribute']['src'])) {
23945  if ($tag['attribute']['src']{0} === '@') {
23946  // data stream
23947  $tag['attribute']['src'] = '@'.base64_decode(substr($tag['attribute']['src'], 1));
23948  $type = '';
23949  } else {
23950  // check for images without protocol
23951  if (preg_match('%^/{2}%', $tag['attribute']['src'])) {
23952  $tag['attribute']['src'] = 'http:'.$tag['attribute']['src'];
23953  }
23954  // replace relative path with real server path
23955  if (($tag['attribute']['src'][0] == '/') AND !empty($_SERVER['DOCUMENT_ROOT']) AND ($_SERVER['DOCUMENT_ROOT'] != '/')) {
23956  $findroot = strpos($tag['attribute']['src'], $_SERVER['DOCUMENT_ROOT']);
23957  if (($findroot === false) OR ($findroot > 1)) {
23958  if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') {
23959  $tag['attribute']['src'] = substr($_SERVER['DOCUMENT_ROOT'], 0, -1).$tag['attribute']['src'];
23960  } else {
23961  $tag['attribute']['src'] = $_SERVER['DOCUMENT_ROOT'].$tag['attribute']['src'];
23962  }
23963  }
23964  }
23965  $tag['attribute']['src'] = htmlspecialchars_decode(urldecode($tag['attribute']['src']));
23966  $type = $this->getImageFileType($tag['attribute']['src']);
23967  $testscrtype = @parse_url($tag['attribute']['src']);
23968  if (!isset($testscrtype['query']) OR empty($testscrtype['query'])) {
23969  // convert URL to server path
23970  $tag['attribute']['src'] = str_replace(K_PATH_URL, K_PATH_MAIN, $tag['attribute']['src']);
23971  }
23972  }
23973  if (!isset($tag['width'])) {
23974  $tag['width'] = 0;
23975  }
23976  if (!isset($tag['height'])) {
23977  $tag['height'] = 0;
23978  }
23979  //if (!isset($tag['attribute']['align'])) {
23980  // the only alignment supported is "bottom"
23981  // further development is required for other modes.
23982  $tag['attribute']['align'] = 'bottom';
23983  //}
23984  switch($tag['attribute']['align']) {
23985  case 'top': {
23986  $align = 'T';
23987  break;
23988  }
23989  case 'middle': {
23990  $align = 'M';
23991  break;
23992  }
23993  case 'bottom': {
23994  $align = 'B';
23995  break;
23996  }
23997  default: {
23998  $align = 'B';
23999  break;
24000  }
24001  }
24002  $prevy = $this->y;
24003  $xpos = $this->x;
24004  $imglink = '';
24005  if (isset($this->HREF['url']) AND !$this->empty_string($this->HREF['url'])) {
24006  $imglink = $this->HREF['url'];
24007  if ($imglink{0} == '#') {
24008  // convert url to internal link
24009  $lnkdata = explode(',', $imglink);
24010  if (isset($lnkdata[0])) {
24011  $page = intval(substr($lnkdata[0], 1));
24012  if (empty($page) OR ($page <= 0)) {
24013  $page = $this->page;
24014  }
24015  if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
24016  $lnky = floatval($lnkdata[1]);
24017  } else {
24018  $lnky = 0;
24019  }
24020  $imglink = $this->AddLink();
24021  $this->SetLink($imglink, $lnky, $page);
24022  }
24023  }
24024  }
24025  $border = 0;
24026  if (isset($tag['border']) AND !empty($tag['border'])) {
24027  // currently only support 1 (frame) or a combination of 'LTRB'
24028  $border = $tag['border'];
24029  }
24030  $iw = '';
24031  if (isset($tag['width'])) {
24032  $iw = $this->getHTMLUnitToUnits($tag['width'], 1, 'px', false);
24033  }
24034  $ih = '';
24035  if (isset($tag['height'])) {
24036  $ih = $this->getHTMLUnitToUnits($tag['height'], 1, 'px', false);
24037  }
24038  if (($type == 'eps') OR ($type == 'ai')) {
24039  $this->ImageEps($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, true, $align, '', $border, true);
24040  } elseif ($type == 'svg') {
24041  $this->ImageSVG($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, $align, '', $border, true);
24042  } else {
24043  $this->Image($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border, false, false, true);
24044  }
24045  switch($align) {
24046  case 'T': {
24047  $this->y = $prevy;
24048  break;
24049  }
24050  case 'M': {
24051  $this->y = (($this->img_rb_y + $prevy - ($tag['fontsize'] / $this->k)) / 2) ;
24052  break;
24053  }
24054  case 'B': {
24055  $this->y = $this->img_rb_y - ($tag['fontsize'] / $this->k);
24056  break;
24057  }
24058  }
24059  }
24060  break;
24061  }
24062  case 'dl': {
24063  ++$this->listnum;
24064  if ($this->listnum == 1) {
24065  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
24066  } else {
24067  $this->addHTMLVertSpace(0, 0, $cell, $firsttag);
24068  }
24069  break;
24070  }
24071  case 'dt': {
24072  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
24073  break;
24074  }
24075  case 'dd': {
24076  if ($this->rtl) {
24077  $this->rMargin += $this->listindent;
24078  } else {
24079  $this->lMargin += $this->listindent;
24080  }
24081  ++$this->listindentlevel;
24082  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
24083  break;
24084  }
24085  case 'ul':
24086  case 'ol': {
24087  ++$this->listnum;
24088  if ($tag['value'] == 'ol') {
24089  $this->listordered[$this->listnum] = true;
24090  } else {
24091  $this->listordered[$this->listnum] = false;
24092  }
24093  if (isset($tag['attribute']['start'])) {
24094  $this->listcount[$this->listnum] = intval($tag['attribute']['start']) - 1;
24095  } else {
24096  $this->listcount[$this->listnum] = 0;
24097  }
24098  if ($this->rtl) {
24099  $this->rMargin += $this->listindent;
24100  $this->x -= $this->listindent;
24101  } else {
24102  $this->lMargin += $this->listindent;
24103  $this->x += $this->listindent;
24104  }
24105  ++$this->listindentlevel;
24106  if ($this->listnum == 1) {
24107  if ($key > 1) {
24108  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
24109  }
24110  } else {
24111  $this->addHTMLVertSpace(0, 0, $cell, $firsttag);
24112  }
24113  break;
24114  }
24115  case 'li': {
24116  if ($key > 2) {
24117  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
24118  }
24119  if ($this->listordered[$this->listnum]) {
24120  // ordered item
24121  if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) {
24122  $this->lispacer = $parent['attribute']['type'];
24123  } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) {
24124  $this->lispacer = $parent['listtype'];
24125  } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) {
24126  $this->lispacer = $this->lisymbol;
24127  } else {
24128  $this->lispacer = '#';
24129  }
24130  ++$this->listcount[$this->listnum];
24131  if (isset($tag['attribute']['value'])) {
24132  $this->listcount[$this->listnum] = intval($tag['attribute']['value']);
24133  }
24134  } else {
24135  // unordered item
24136  if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) {
24137  $this->lispacer = $parent['attribute']['type'];
24138  } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) {
24139  $this->lispacer = $parent['listtype'];
24140  } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) {
24141  $this->lispacer = $this->lisymbol;
24142  } else {
24143  $this->lispacer = '!';
24144  }
24145  }
24146  break;
24147  }
24148  case 'blockquote': {
24149  if ($this->rtl) {
24150  $this->rMargin += $this->listindent;
24151  } else {
24152  $this->lMargin += $this->listindent;
24153  }
24154  ++$this->listindentlevel;
24155  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
24156  break;
24157  }
24158  case 'br': {
24159  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
24160  break;
24161  }
24162  case 'div': {
24163  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
24164  break;
24165  }
24166  case 'p': {
24167  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
24168  break;
24169  }
24170  case 'pre': {
24171  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
24172  $this->premode = true;
24173  break;
24174  }
24175  case 'sup': {
24176  $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k));
24177  break;
24178  }
24179  case 'sub': {
24180  $this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k));
24181  break;
24182  }
24183  case 'h1':
24184  case 'h2':
24185  case 'h3':
24186  case 'h4':
24187  case 'h5':
24188  case 'h6': {
24189  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
24190  break;
24191  }
24192  // Form fields (since 4.8.000 - 2009-09-07)
24193  case 'form': {
24194  if (isset($tag['attribute']['action'])) {
24195  $this->form_action = $tag['attribute']['action'];
24196  } else {
24197  $this->form_action = K_PATH_URL.$_SERVER['SCRIPT_NAME'];
24198  }
24199  if (isset($tag['attribute']['enctype'])) {
24200  $this->form_enctype = $tag['attribute']['enctype'];
24201  } else {
24202  $this->form_enctype = 'application/x-www-form-urlencoded';
24203  }
24204  if (isset($tag['attribute']['method'])) {
24205  $this->form_mode = $tag['attribute']['method'];
24206  } else {
24207  $this->form_mode = 'post';
24208  }
24209  break;
24210  }
24211  case 'input': {
24212  if (isset($tag['attribute']['name']) AND !$this->empty_string($tag['attribute']['name'])) {
24213  $name = $tag['attribute']['name'];
24214  } else {
24215  break;
24216  }
24217  $prop = array();
24218  $opt = array();
24219  if (isset($tag['attribute']['readonly']) AND !$this->empty_string($tag['attribute']['readonly'])) {
24220  $prop['readonly'] = true;
24221  }
24222  if (isset($tag['attribute']['value']) AND !$this->empty_string($tag['attribute']['value'])) {
24223  $value = $tag['attribute']['value'];
24224  }
24225  if (isset($tag['attribute']['maxlength']) AND !$this->empty_string($tag['attribute']['maxlength'])) {
24226  $opt['maxlen'] = intval($tag['attribute']['maxlength']);
24227  }
24228  $h = $this->FontSize * $this->cell_height_ratio;
24229  if (isset($tag['attribute']['size']) AND !$this->empty_string($tag['attribute']['size'])) {
24230  $w = intval($tag['attribute']['size']) * $this->GetStringWidth(chr(32)) * 2;
24231  } else {
24232  $w = $h;
24233  }
24234  if (isset($tag['attribute']['checked']) AND (($tag['attribute']['checked'] == 'checked') OR ($tag['attribute']['checked'] == 'true'))) {
24235  $checked = true;
24236  } else {
24237  $checked = false;
24238  }
24239  if (isset($tag['align'])) {
24240  switch ($tag['align']) {
24241  case 'C': {
24242  $opt['q'] = 1;
24243  break;
24244  }
24245  case 'R': {
24246  $opt['q'] = 2;
24247  break;
24248  }
24249  case 'L':
24250  default: {
24251  break;
24252  }
24253  }
24254  }
24255  switch ($tag['attribute']['type']) {
24256  case 'text': {
24257  if (isset($value)) {
24258  $opt['v'] = $value;
24259  }
24260  $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
24261  break;
24262  }
24263  case 'password': {
24264  if (isset($value)) {
24265  $opt['v'] = $value;
24266  }
24267  $prop['password'] = 'true';
24268  $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
24269  break;
24270  }
24271  case 'checkbox': {
24272  if (!isset($value)) {
24273  break;
24274  }
24275  $this->CheckBox($name, $w, $checked, $prop, $opt, $value, '', '', false);
24276  break;
24277  }
24278  case 'radio': {
24279  if (!isset($value)) {
24280  break;
24281  }
24282  $this->RadioButton($name, $w, $prop, $opt, $value, $checked, '', '', false);
24283  break;
24284  }
24285  case 'submit': {
24286  if (!isset($value)) {
24287  $value = 'submit';
24288  }
24289  $w = $this->GetStringWidth($value) * 1.5;
24290  $h *= 1.6;
24291  $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
24292  $action = array();
24293  $action['S'] = 'SubmitForm';
24294  $action['F'] = $this->form_action;
24295  if ($this->form_enctype != 'FDF') {
24296  $action['Flags'] = array('ExportFormat');
24297  }
24298  if ($this->form_mode == 'get') {
24299  $action['Flags'] = array('GetMethod');
24300  }
24301  $this->Button($name, $w, $h, $value, $action, $prop, $opt, '', '', false);
24302  break;
24303  }
24304  case 'reset': {
24305  if (!isset($value)) {
24306  $value = 'reset';
24307  }
24308  $w = $this->GetStringWidth($value) * 1.5;
24309  $h *= 1.6;
24310  $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
24311  $this->Button($name, $w, $h, $value, array('S'=>'ResetForm'), $prop, $opt, '', '', false);
24312  break;
24313  }
24314  case 'file': {
24315  $prop['fileSelect'] = 'true';
24316  $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
24317  if (!isset($value)) {
24318  $value = '*';
24319  }
24320  $w = $this->GetStringWidth($value) * 2;
24321  $h *= 1.2;
24322  $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
24323  $jsaction = 'var f=this.getField(\''.$name.'\'); f.browseForFileToSubmit();';
24324  $this->Button('FB_'.$name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
24325  break;
24326  }
24327  case 'hidden': {
24328  if (isset($value)) {
24329  $opt['v'] = $value;
24330  }
24331  $opt['f'] = array('invisible', 'hidden');
24332  $this->TextField($name, 0, 0, $prop, $opt, '', '', false);
24333  break;
24334  }
24335  case 'image': {
24336  // THIS TYPE MUST BE FIXED
24337  if (isset($tag['attribute']['src']) AND !$this->empty_string($tag['attribute']['src'])) {
24338  $img = $tag['attribute']['src'];
24339  } else {
24340  break;
24341  }
24342  $value = 'img';
24343  //$opt['mk'] = array('i'=>$img, 'tp'=>1, 'if'=>array('sw'=>'A', 's'=>'A', 'fb'=>false));
24344  if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
24345  $jsaction = $tag['attribute']['onclick'];
24346  } else {
24347  $jsaction = '';
24348  }
24349  $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
24350  break;
24351  }
24352  case 'button': {
24353  if (!isset($value)) {
24354  $value = ' ';
24355  }
24356  $w = $this->GetStringWidth($value) * 1.5;
24357  $h *= 1.6;
24358  $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
24359  if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
24360  $jsaction = $tag['attribute']['onclick'];
24361  } else {
24362  $jsaction = '';
24363  }
24364  $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
24365  break;
24366  }
24367  }
24368  break;
24369  }
24370  case 'textarea': {
24371  $prop = array();
24372  $opt = array();
24373  if (isset($tag['attribute']['readonly']) AND !$this->empty_string($tag['attribute']['readonly'])) {
24374  $prop['readonly'] = true;
24375  }
24376  if (isset($tag['attribute']['name']) AND !$this->empty_string($tag['attribute']['name'])) {
24377  $name = $tag['attribute']['name'];
24378  } else {
24379  break;
24380  }
24381  if (isset($tag['attribute']['value']) AND !$this->empty_string($tag['attribute']['value'])) {
24382  $opt['v'] = $tag['attribute']['value'];
24383  }
24384  if (isset($tag['attribute']['cols']) AND !$this->empty_string($tag['attribute']['cols'])) {
24385  $w = intval($tag['attribute']['cols']) * $this->GetStringWidth(chr(32)) * 2;
24386  } else {
24387  $w = 40;
24388  }
24389  if (isset($tag['attribute']['rows']) AND !$this->empty_string($tag['attribute']['rows'])) {
24390  $h = intval($tag['attribute']['rows']) * $this->FontSize * $this->cell_height_ratio;
24391  } else {
24392  $h = 10;
24393  }
24394  $prop['multiline'] = 'true';
24395  $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
24396  break;
24397  }
24398  case 'select': {
24399  $h = $this->FontSize * $this->cell_height_ratio;
24400  if (isset($tag['attribute']['size']) AND !$this->empty_string($tag['attribute']['size'])) {
24401  $h *= ($tag['attribute']['size'] + 1);
24402  }
24403  $prop = array();
24404  $opt = array();
24405  if (isset($tag['attribute']['name']) AND !$this->empty_string($tag['attribute']['name'])) {
24406  $name = $tag['attribute']['name'];
24407  } else {
24408  break;
24409  }
24410  $w = 0;
24411  if (isset($tag['attribute']['opt']) AND !$this->empty_string($tag['attribute']['opt'])) {
24412  $options = explode('#!NwL!#', $tag['attribute']['opt']);
24413  $values = array();
24414  foreach ($options as $val) {
24415  if (strpos($val, '#!TaB!#') !== false) {
24416  $opts = explode('#!TaB!#', $val);
24417  $values[] = $opts;
24418  $w = max($w, $this->GetStringWidth($opts[1]));
24419  } else {
24420  $values[] = $val;
24421  $w = max($w, $this->GetStringWidth($val));
24422  }
24423  }
24424  } else {
24425  break;
24426  }
24427  $w *= 2;
24428  if (isset($tag['attribute']['multiple']) AND ($tag['attribute']['multiple']='multiple')) {
24429  $prop['multipleSelection'] = 'true';
24430  $this->ListBox($name, $w, $h, $values, $prop, $opt, '', '', false);
24431  } else {
24432  $this->ComboBox($name, $w, $h, $values, $prop, $opt, '', '', false);
24433  }
24434  break;
24435  }
24436  case 'tcpdf': {
24437  if (defined('K_TCPDF_CALLS_IN_HTML') AND (K_TCPDF_CALLS_IN_HTML === true)) {
24438  // Special tag used to call TCPDF methods
24439  if (isset($tag['attribute']['method'])) {
24440  $tcpdf_method = $tag['attribute']['method'];
24441  if (method_exists($this, $tcpdf_method)) {
24442  if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) {
24443  $params = unserialize(urldecode($tag['attribute']['params']));
24444  call_user_func_array(array($this, $tcpdf_method), $params);
24445  } else {
24446  $this->$tcpdf_method();
24447  }
24448  $this->newline = true;
24449  }
24450  }
24451  }
24452  break;
24453  }
24454  default: {
24455  break;
24456  }
24457  }
24458  // define tags that support borders and background colors
24459  $bordertags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table');
24460  if (in_array($tag['value'], $bordertags)) {
24461  // set border
24462  $dom[$key]['borderposition'] = $this->getBorderStartPosition();
24463  }
24464  if ($dom[$key]['self'] AND isset($dom[$key]['attribute']['pagebreakafter'])) {
24465  $pba = $dom[$key]['attribute']['pagebreakafter'];
24466  // check for pagebreak
24467  if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
24468  // add a page (or trig AcceptPageBreak() for multicolumn mode)
24469  $this->checkPageBreak($this->PageBreakTrigger + 1);
24470  }
24471  if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
24472  OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
24473  // add a page (or trig AcceptPageBreak() for multicolumn mode)
24474  $this->checkPageBreak($this->PageBreakTrigger + 1);
24475  }
24476  }
24477  return $dom;
24478  }
24479 
24489  protected function closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney=0) {
24490  $tag = $dom[$key];
24491  $parent = $dom[($dom[$key]['parent'])];
24492  $lasttag = ((!isset($dom[($key + 1)])) OR ((!isset($dom[($key + 2)])) AND ($dom[($key + 1)]['value'] == 'marker')));
24493  $in_table_head = false;
24494  // maximum x position (used to draw borders)
24495  if ($this->rtl) {
24496  $xmax = $this->w;
24497  } else {
24498  $xmax = 0;
24499  }
24500  if ($tag['block']) {
24501  $hbz = 0; // distance from y to line bottom
24502  $hb = 0; // vertical space between block tags
24503  // calculate vertical space for block tags
24504  if (isset($this->tagvspaces[$tag['value']][1]['h']) AND ($this->tagvspaces[$tag['value']][1]['h'] >= 0)) {
24505  $pre_h = $this->tagvspaces[$tag['value']][1]['h'];
24506  } elseif (isset($parent['fontsize'])) {
24507  $pre_h = (($parent['fontsize'] / $this->k) * $this->cell_height_ratio);
24508  } else {
24509  $pre_h = $this->FontSize * $this->cell_height_ratio;
24510  }
24511  if (isset($this->tagvspaces[$tag['value']][1]['n'])) {
24512  $n = $this->tagvspaces[$tag['value']][1]['n'];
24513  } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
24514  $n = 0.6;
24515  } else {
24516  $n = 1;
24517  }
24518  if ((!isset($this->tagvspaces[$tag['value']])) AND ($tag['value'] == 'div')) {
24519  $hb = 0;
24520  } else {
24521  $hb = ($n * $pre_h);
24522  }
24523  if ($maxbottomliney > $this->PageBreakTrigger) {
24524  $hbz = ($this->FontSize * $this->cell_height_ratio);
24525  } elseif ($this->y < $maxbottomliney) {
24526  $hbz = ($maxbottomliney - $this->y);
24527  }
24528  }
24529  // Closing tag
24530  switch($tag['value']) {
24531  case 'tr': {
24532  $table_el = $dom[($dom[$key]['parent'])]['parent'];
24533  if (!isset($parent['endy'])) {
24534  $dom[($dom[$key]['parent'])]['endy'] = $this->y;
24535  $parent['endy'] = $this->y;
24536  }
24537  if (!isset($parent['endpage'])) {
24538  $dom[($dom[$key]['parent'])]['endpage'] = $this->page;
24539  $parent['endpage'] = $this->page;
24540  }
24541  if (!isset($parent['endcolumn'])) {
24542  $dom[($dom[$key]['parent'])]['endcolumn'] = $this->current_column;
24543  $parent['endcolumn'] = $this->current_column;
24544  }
24545  // update row-spanned cells
24546  if (isset($dom[$table_el]['rowspans'])) {
24547  foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
24548  $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1;
24549  if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
24550  if (($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) AND ($dom[$table_el]['rowspans'][$k]['endcolumn'] == $parent['endcolumn'])) {
24551  $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']);
24552  } elseif (($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) OR ($dom[$table_el]['rowspans'][$k]['endcolumn'] > $parent['endcolumn'])) {
24553  $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
24554  $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
24555  $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
24556  }
24557  }
24558  }
24559  // report new endy and endpage to the rowspanned cells
24560  foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
24561  if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
24562  $dom[$table_el]['rowspans'][$k]['endpage'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']);
24563  $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
24564  $dom[$table_el]['rowspans'][$k]['endcolumn'] = max($dom[$table_el]['rowspans'][$k]['endcolumn'], $dom[($dom[$key]['parent'])]['endcolumn']);
24565  $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
24566  $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']);
24567  $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
24568  }
24569  }
24570  // update remaining rowspanned cells
24571  foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
24572  if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
24573  $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage'];
24574  $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[($dom[$key]['parent'])]['endcolumn'];
24575  $dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy'];
24576  }
24577  }
24578  }
24579  $this->setPage($dom[($dom[$key]['parent'])]['endpage']);
24580  if ($this->num_columns > 1) {
24581  $this->selectColumn($dom[($dom[$key]['parent'])]['endcolumn']);
24582  }
24583  $this->y = $dom[($dom[$key]['parent'])]['endy'];
24584  if (isset($dom[$table_el]['attribute']['cellspacing'])) {
24585  $this->y += $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px');
24586  } elseif (isset($dom[$table_el]['border-spacing'])) {
24587  $this->y += $dom[$table_el]['border-spacing']['V'];
24588  }
24589  $this->Ln(0, $cell);
24590  if ($this->current_column == $parent['startcolumn']) {
24591  $this->x = $parent['startx'];
24592  }
24593  // account for booklet mode
24594  if ($this->page > $parent['startpage']) {
24595  if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$parent['startpage']]['orm'])) {
24596  $this->x -= ($this->pagedim[$this->page]['orm'] - $this->pagedim[$parent['startpage']]['orm']);
24597  } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$parent['startpage']]['olm'])) {
24598  $this->x += ($this->pagedim[$this->page]['olm'] - $this->pagedim[$parent['startpage']]['olm']);
24599  }
24600  }
24601  break;
24602  }
24603  case 'tablehead':
24604  // closing tag used for the thead part
24605  $in_table_head = true;
24606  $this->inthead = false;
24607  case 'table': {
24608  $table_el = $parent;
24609  // set default border
24610  if (isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0)) {
24611  // set default border
24612  $border = array('LTRB' => array('width' => $this->getCSSBorderWidth($table_el['attribute']['border']), 'cap'=>'square', 'join'=>'miter', 'dash'=> 0, 'color'=>array(0,0,0)));
24613  } else {
24614  $border = 0;
24615  }
24616  $default_border = $border;
24617  // fix bottom line alignment of last line before page break
24618  foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) {
24619  // update row-spanned cells
24620  if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
24621  foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
24622  if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] > 0)) {
24623  $dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey;
24624  }
24625  if ($dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] == $trkey) {
24626  $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1;
24627  }
24628  }
24629  }
24630  if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) {
24631  $pgendy = $this->pagedim[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim[$dom[$prevtrkey]['endpage']]['bm'];
24632  $dom[$prevtrkey]['endy'] = $pgendy;
24633  // update row-spanned cells
24634  if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
24635  foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
24636  if (($trwsp['trid'] == $trkey) AND ($trwsp['mrowspan'] > 1) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) {
24637  $dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy;
24638  $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1;
24639  }
24640  }
24641  }
24642  }
24643  $prevtrkey = $trkey;
24644  $table_el = $dom[($dom[$key]['parent'])];
24645  }
24646  // for each row
24647  if (count($table_el['trids']) > 0) {
24648  unset($xmax);
24649  }
24650  foreach ($table_el['trids'] as $j => $trkey) {
24651  $parent = $dom[$trkey];
24652  if (!isset($xmax)) {
24653  $xmax = $parent['cellpos'][(count($parent['cellpos']) - 1)]['endx'];
24654  }
24655  // for each cell on the row
24656  foreach ($parent['cellpos'] as $k => $cellpos) {
24657  if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) {
24658  $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx'];
24659  $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx'];
24660  $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy'];
24661  $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage'];
24662  $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage'];
24663  $startcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['startcolumn'];
24664  $endcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['endcolumn'];
24665  } else {
24666  $endy = $parent['endy'];
24667  $startpage = $parent['startpage'];
24668  $endpage = $parent['endpage'];
24669  $startcolumn = $parent['startcolumn'];
24670  $endcolumn = $parent['endcolumn'];
24671  }
24672  if ($this->num_columns == 0) {
24673  $this->num_columns = 1;
24674  }
24675  if (isset($cellpos['border'])) {
24676  $border = $cellpos['border'];
24677  }
24678  if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
24679  $this->SetFillColorArray($cellpos['bgcolor']);
24680  $fill = true;
24681  } else {
24682  $fill = false;
24683  }
24684  $x = $cellpos['startx'];
24685  $y = $parent['starty'];
24686  $starty = $y;
24687  $w = abs($cellpos['endx'] - $cellpos['startx']);
24688  // get border modes
24689  $border_start = $this->getBorderMode($border, $position='start');
24690  $border_end = $this->getBorderMode($border, $position='end');
24691  $border_middle = $this->getBorderMode($border, $position='middle');
24692  // design borders around HTML cells.
24693  for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
24694  $ccode = '';
24695  $this->setPage($page);
24696  if ($this->num_columns < 2) {
24697  // single-column mode
24698  $this->x = $x;
24699  $this->y = $this->tMargin;
24700  }
24701  // account for margin changes
24702  if ($page > $startpage) {
24703  if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
24704  $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
24705  } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
24706  $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
24707  }
24708  }
24709  if ($startpage == $endpage) { // single page
24710  $deltacol = 0;
24711  $deltath = 0;
24712  for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
24713  $this->selectColumn($column);
24714  if ($startcolumn == $endcolumn) { // single column
24715  $cborder = $border;
24716  $h = $endy - $parent['starty'];
24717  $this->y = $y;
24718  $this->x = $x;
24719  } elseif ($column == $startcolumn) { // first column
24720  $cborder = $border_start;
24721  $this->y = $starty;
24722  $this->x = $x;
24723  $h = $this->h - $this->y - $this->bMargin;
24724  if ($this->rtl) {
24725  $deltacol = $this->x + $this->rMargin - $this->w;
24726  } else {
24727  $deltacol = $this->x - $this->lMargin;
24728  }
24729  } elseif ($column == $endcolumn) { // end column
24730  $cborder = $border_end;
24731  if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
24732  $this->y = $this->columns[$column]['th']['\''.$page.'\''];
24733  }
24734  $this->x += $deltacol;
24735  $h = $endy - $this->y;
24736  } else { // middle column
24737  $cborder = $border_middle;
24738  if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
24739  $this->y = $this->columns[$column]['th']['\''.$page.'\''];
24740  }
24741  $this->x += $deltacol;
24742  $h = $this->h - $this->y - $this->bMargin;
24743  }
24744  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
24745  } // end for each column
24746  } elseif ($page == $startpage) { // first page
24747  $deltacol = 0;
24748  $deltath = 0;
24749  for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
24750  $this->selectColumn($column);
24751  if ($column == $startcolumn) { // first column
24752  $cborder = $border_start;
24753  $this->y = $starty;
24754  $this->x = $x;
24755  $h = $this->h - $this->y - $this->bMargin;
24756  if ($this->rtl) {
24757  $deltacol = $this->x + $this->rMargin - $this->w;
24758  } else {
24759  $deltacol = $this->x - $this->lMargin;
24760  }
24761  } else { // middle column
24762  $cborder = $border_middle;
24763  if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
24764  $this->y = $this->columns[$column]['th']['\''.$page.'\''];
24765  }
24766  $this->x += $deltacol;
24767  $h = $this->h - $this->y - $this->bMargin;
24768  }
24769  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
24770  } // end for each column
24771  } elseif ($page == $endpage) { // last page
24772  $deltacol = 0;
24773  $deltath = 0;
24774  for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
24775  $this->selectColumn($column);
24776  if ($column == $endcolumn) { // end column
24777  $cborder = $border_end;
24778  if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
24779  $this->y = $this->columns[$column]['th']['\''.$page.'\''];
24780  }
24781  $this->x += $deltacol;
24782  $h = $endy - $this->y;
24783  } else { // middle column
24784  $cborder = $border_middle;
24785  if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
24786  $this->y = $this->columns[$column]['th']['\''.$page.'\''];
24787  }
24788  $this->x += $deltacol;
24789  $h = $this->h - $this->y - $this->bMargin;
24790  }
24791  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
24792  } // end for each column
24793  } else { // middle page
24794  $deltacol = 0;
24795  $deltath = 0;
24796  for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
24797  $this->selectColumn($column);
24798  $cborder = $border_middle;
24799  if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
24800  $this->y = $this->columns[$column]['th']['\''.$page.'\''];
24801  }
24802  $this->x += $deltacol;
24803  $h = $this->h - $this->y - $this->bMargin;
24804  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
24805  } // end for each column
24806  }
24807  if ($cborder OR $fill) {
24808  $offsetlen = strlen($ccode);
24809  // draw border and fill
24810  if ($this->inxobj) {
24811  // we are inside an XObject template
24812  if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
24813  $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
24814  $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
24815  $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
24816  } else {
24817  $pagemark = $this->xobjects[$this->xobjid]['intmrk'];
24818  $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
24819  }
24820  $pagebuff = $this->xobjects[$this->xobjid]['outdata'];
24821  $pstart = substr($pagebuff, 0, $pagemark);
24822  $pend = substr($pagebuff, $pagemark);
24823  $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
24824  } else {
24825  // draw border and fill
24826  if (end($this->transfmrk[$this->page]) !== false) {
24827  $pagemarkkey = key($this->transfmrk[$this->page]);
24828  $pagemark = $this->transfmrk[$this->page][$pagemarkkey];
24829  } elseif ($this->InFooter) {
24830  $pagemark = $this->footerpos[$this->page];
24831  } else {
24832  $pagemark = $this->intmrk[$this->page];
24833  }
24834  $pagebuff = $this->getPageBuffer($this->page);
24835  $pstart = substr($pagebuff, 0, $pagemark);
24836  $pend = substr($pagebuff, $pagemark);
24837  $this->setPageBuffer($this->page, $pstart.$ccode.$pend);
24838  }
24839  }
24840  } // end for each page
24841  // restore default border
24842  $border = $default_border;
24843  } // end for each cell on the row
24844  if (isset($table_el['attribute']['cellspacing'])) {
24845  $this->y += $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px');
24846  } elseif (isset($table_el['border-spacing'])) {
24847  $this->y += $table_el['border-spacing']['V'];
24848  }
24849  $this->Ln(0, $cell);
24850  $this->x = $parent['startx'];
24851  if ($endpage > $startpage) {
24852  if (($this->rtl) AND ($this->pagedim[$endpage]['orm'] != $this->pagedim[$startpage]['orm'])) {
24853  $this->x += ($this->pagedim[$endpage]['orm'] - $this->pagedim[$startpage]['orm']);
24854  } elseif ((!$this->rtl) AND ($this->pagedim[$endpage]['olm'] != $this->pagedim[$startpage]['olm'])) {
24855  $this->x += ($this->pagedim[$endpage]['olm'] - $this->pagedim[$startpage]['olm']);
24856  }
24857  }
24858  }
24859  if (!$in_table_head) { // we are not inside a thead section
24860  $this->cell_padding = $table_el['old_cell_padding'];
24861  // reset row height
24862  $this->resetLastH();
24863  if (($this->page == ($this->numpages - 1)) AND ($this->pageopen[$this->numpages])) {
24864  $plendiff = ($this->pagelen[$this->numpages] - $this->emptypagemrk[$this->numpages]);
24865  if (($plendiff > 0) AND ($plendiff < 60)) {
24866  $pagediff = substr($this->getPageBuffer($this->numpages), $this->emptypagemrk[$this->numpages], $plendiff);
24867  if (substr($pagediff, 0, 5) == 'BT /F') {
24868  // the difference is only a font setting
24869  $plendiff = 0;
24870  }
24871  }
24872  if ($plendiff == 0) {
24873  // remove last blank page
24874  $this->deletePage($this->numpages);
24875  }
24876  }
24877  if (isset($this->theadMargins['top'])) {
24878  // restore top margin
24879  $this->tMargin = $this->theadMargins['top'];
24880  }
24881  if (!isset($table_el['attribute']['nested']) OR ($table_el['attribute']['nested'] != 'true')) {
24882  // reset main table header
24883  $this->thead = '';
24884  $this->theadMargins = array();
24885  $this->pagedim[$this->page]['tm'] = $this->tMargin;
24886  }
24887  }
24888  $parent = $table_el;
24889  break;
24890  }
24891  case 'a': {
24892  $this->HREF = '';
24893  break;
24894  }
24895  case 'sup': {
24896  $this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k));
24897  break;
24898  }
24899  case 'sub': {
24900  $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize']) / $this->k));
24901  break;
24902  }
24903  case 'div': {
24904  $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
24905  break;
24906  }
24907  case 'blockquote': {
24908  if ($this->rtl) {
24909  $this->rMargin -= $this->listindent;
24910  } else {
24911  $this->lMargin -= $this->listindent;
24912  }
24913  --$this->listindentlevel;
24914  $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
24915  break;
24916  }
24917  case 'p': {
24918  $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
24919  break;
24920  }
24921  case 'pre': {
24922  $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
24923  $this->premode = false;
24924  break;
24925  }
24926  case 'dl': {
24927  --$this->listnum;
24928  if ($this->listnum <= 0) {
24929  $this->listnum = 0;
24930  $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
24931  } else {
24932  $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
24933  }
24934  $this->resetLastH();
24935  break;
24936  }
24937  case 'dt': {
24938  $this->lispacer = '';
24939  $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
24940  break;
24941  }
24942  case 'dd': {
24943  $this->lispacer = '';
24944  if ($this->rtl) {
24945  $this->rMargin -= $this->listindent;
24946  } else {
24947  $this->lMargin -= $this->listindent;
24948  }
24949  --$this->listindentlevel;
24950  $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
24951  break;
24952  }
24953  case 'ul':
24954  case 'ol': {
24955  --$this->listnum;
24956  $this->lispacer = '';
24957  if ($this->rtl) {
24958  $this->rMargin -= $this->listindent;
24959  } else {
24960  $this->lMargin -= $this->listindent;
24961  }
24962  --$this->listindentlevel;
24963  if ($this->listnum <= 0) {
24964  $this->listnum = 0;
24965  $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
24966  } else {
24967  $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
24968  }
24969  $this->resetLastH();
24970  break;
24971  }
24972  case 'li': {
24973  $this->lispacer = '';
24974  $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
24975  break;
24976  }
24977  case 'h1':
24978  case 'h2':
24979  case 'h3':
24980  case 'h4':
24981  case 'h5':
24982  case 'h6': {
24983  $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
24984  break;
24985  }
24986  // Form fields (since 4.8.000 - 2009-09-07)
24987  case 'form': {
24988  $this->form_action = '';
24989  $this->form_enctype = 'application/x-www-form-urlencoded';
24990  break;
24991  }
24992  default : {
24993  break;
24994  }
24995  }
24996  // draw border and background (if any)
24997  $this->drawHTMLTagBorder($parent, $xmax);
24998  if (isset($dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'])) {
24999  $pba = $dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'];
25000  // check for pagebreak
25001  if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
25002  // add a page (or trig AcceptPageBreak() for multicolumn mode)
25003  $this->checkPageBreak($this->PageBreakTrigger + 1);
25004  }
25005  if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
25006  OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
25007  // add a page (or trig AcceptPageBreak() for multicolumn mode)
25008  $this->checkPageBreak($this->PageBreakTrigger + 1);
25009  }
25010  }
25011  $this->tmprtl = false;
25012  return $dom;
25013  }
25014 
25024  protected function addHTMLVertSpace($hbz=0, $hb=0, $cell=false, $firsttag=false, $lasttag=false) {
25025  if ($firsttag) {
25026  $this->Ln(0, $cell);
25027  $this->htmlvspace = 0;
25028  return;
25029  }
25030  if ($lasttag) {
25031  $this->Ln($hbz, $cell);
25032  $this->htmlvspace = 0;
25033  return;
25034  }
25035  if ($hb < $this->htmlvspace) {
25036  $hd = 0;
25037  } else {
25038  $hd = $hb - $this->htmlvspace;
25039  $this->htmlvspace = $hb;
25040  }
25041  $this->Ln(($hbz + $hd), $cell);
25042  }
25043 
25050  protected function getBorderStartPosition() {
25051  if ($this->rtl) {
25052  $xmax = $this->lMargin;
25053  } else {
25054  $xmax = $this->w - $this->rMargin;
25055  }
25056  return array('page' => $this->page, 'column' => $this->current_column, 'x' => $this->x, 'y' => $this->y, 'xmax' => $xmax);
25057  }
25058 
25066  protected function drawHTMLTagBorder($tag, $xmax) {
25067  if (!isset($tag['borderposition'])) {
25068  // nothing to draw
25069  return;
25070  }
25071  $prev_x = $this->x;
25072  $prev_y = $this->y;
25073  $prev_lasth = $this->lasth;
25074  $border = 0;
25075  $fill = false;
25076  $this->lasth = 0;
25077  if (isset($tag['border']) AND !empty($tag['border'])) {
25078  // get border style
25079  $border = $tag['border'];
25080  if (!$this->empty_string($this->thead) AND (!$this->inthead)) {
25081  // border for table header
25082  $border = $this->getBorderMode($border, $position='middle');
25083  }
25084  }
25085  if (isset($tag['bgcolor']) AND ($tag['bgcolor'] !== false)) {
25086  // get background color
25087  $old_bgcolor = $this->bgcolor;
25088  $this->SetFillColorArray($tag['bgcolor']);
25089  $fill = true;
25090  }
25091  if (!$border AND !$fill) {
25092  // nothing to draw
25093  return;
25094  }
25095  if (isset($tag['attribute']['cellspacing'])) {
25096  $clsp = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
25097  $cellspacing = array('H' => $clsp, 'V' => $clsp);
25098  } elseif (isset($tag['border-spacing'])) {
25099  $cellspacing = $tag['border-spacing'];
25100  } else {
25101  $cellspacing = array('H' => 0, 'V' => 0);
25102  }
25103  if (($tag['value'] != 'table') AND (is_array($border)) AND (!empty($border))) {
25104  // draw the border externally respect the sqare edge.
25105  $border['mode'] = 'ext';
25106  }
25107  if ($this->rtl) {
25108  if ($xmax >= $tag['borderposition']['x']) {
25109  $xmax = $tag['borderposition']['xmax'];
25110  }
25111  $w = ($tag['borderposition']['x'] - $xmax);
25112  } else {
25113  if ($xmax <= $tag['borderposition']['x']) {
25114  $xmax = $tag['borderposition']['xmax'];
25115  }
25116  $w = ($xmax - $tag['borderposition']['x']);
25117  }
25118  if ($w <= 0) {
25119  return;
25120  }
25121  $w += $cellspacing['H'];
25122  $startpage = $tag['borderposition']['page'];
25123  $startcolumn = $tag['borderposition']['column'];
25124  $x = $tag['borderposition']['x'];
25125  $y = $tag['borderposition']['y'];
25126  $endpage = $this->page;
25127  $starty = $tag['borderposition']['y'] - $cellspacing['V'];
25128  $currentY = $this->y;
25129  $this->x = $x;
25130  // get latest column
25131  $endcolumn = $this->current_column;
25132  if ($this->num_columns == 0) {
25133  $this->num_columns = 1;
25134  }
25135  // get border modes
25136  $border_start = $this->getBorderMode($border, $position='start');
25137  $border_end = $this->getBorderMode($border, $position='end');
25138  $border_middle = $this->getBorderMode($border, $position='middle');
25139  // temporary disable page regions
25140  $temp_page_regions = $this->page_regions;
25141  $this->page_regions = array();
25142  // design borders around HTML cells.
25143  for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
25144  $ccode = '';
25145  $this->setPage($page);
25146  if ($this->num_columns < 2) {
25147  // single-column mode
25148  $this->x = $x;
25149  $this->y = $this->tMargin;
25150  }
25151  // account for margin changes
25152  if ($page > $startpage) {
25153  if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
25154  $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
25155  } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
25156  $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
25157  }
25158  }
25159  if ($startpage == $endpage) {
25160  // single page
25161  for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
25162  $this->selectColumn($column);
25163  if ($startcolumn == $endcolumn) { // single column
25164  $cborder = $border;
25165  $h = ($currentY - $y) + $cellspacing['V'];
25166  $this->y = $starty;
25167  } elseif ($column == $startcolumn) { // first column
25168  $cborder = $border_start;
25169  $this->y = $starty;
25170  $h = $this->h - $this->y - $this->bMargin;
25171  } elseif ($column == $endcolumn) { // end column
25172  $cborder = $border_end;
25173  $h = $currentY - $this->y;
25174  } else { // middle column
25175  $cborder = $border_middle;
25176  $h = $this->h - $this->y - $this->bMargin;
25177  }
25178  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
25179  } // end for each column
25180  } elseif ($page == $startpage) { // first page
25181  for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
25182  $this->selectColumn($column);
25183  if ($column == $startcolumn) { // first column
25184  $cborder = $border_start;
25185  $this->y = $starty;
25186  $h = $this->h - $this->y - $this->bMargin;
25187  } else { // middle column
25188  $cborder = $border_middle;
25189  $h = $this->h - $this->y - $this->bMargin;
25190  }
25191  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
25192  } // end for each column
25193  } elseif ($page == $endpage) { // last page
25194  for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
25195  $this->selectColumn($column);
25196  if ($column == $endcolumn) {
25197  // end column
25198  $cborder = $border_end;
25199  $h = $currentY - $this->y;
25200  } else {
25201  // middle column
25202  $cborder = $border_middle;
25203  $h = $this->h - $this->y - $this->bMargin;
25204  }
25205  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
25206  } // end for each column
25207  } else { // middle page
25208  for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
25209  $this->selectColumn($column);
25210  $cborder = $border_middle;
25211  $h = $this->h - $this->y - $this->bMargin;
25212  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
25213  } // end for each column
25214  }
25215  if ($cborder OR $fill) {
25216  $offsetlen = strlen($ccode);
25217  // draw border and fill
25218  if ($this->inxobj) {
25219  // we are inside an XObject template
25220  if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
25221  $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
25222  $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
25223  $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
25224  } else {
25225  $pagemark = $this->xobjects[$this->xobjid]['intmrk'];
25226  $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
25227  }
25228  $pagebuff = $this->xobjects[$this->xobjid]['outdata'];
25229  $pstart = substr($pagebuff, 0, $pagemark);
25230  $pend = substr($pagebuff, $pagemark);
25231  $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
25232  } else {
25233  if (end($this->transfmrk[$this->page]) !== false) {
25234  $pagemarkkey = key($this->transfmrk[$this->page]);
25235  $pagemark = $this->transfmrk[$this->page][$pagemarkkey];
25236  } elseif ($this->InFooter) {
25237  $pagemark = $this->footerpos[$this->page];
25238  } else {
25239  $pagemark = $this->intmrk[$this->page];
25240  }
25241  $pagebuff = $this->getPageBuffer($this->page);
25242  $pstart = substr($pagebuff, 0, $pagemark);
25243  $pend = substr($pagebuff, $pagemark);
25244  $this->setPageBuffer($this->page, $pstart.$ccode.$pend);
25245  $this->bordermrk[$this->page] += $offsetlen;
25246  $this->cntmrk[$this->page] += $offsetlen;
25247  }
25248  }
25249  } // end for each page
25250  // restore page regions
25251  $this->page_regions = $temp_page_regions;
25252  if (isset($old_bgcolor)) {
25253  // restore background color
25254  $this->SetFillColorArray($old_bgcolor);
25255  }
25256  // restore pointer position
25257  $this->x = $prev_x;
25258  $this->y = $prev_y;
25259  $this->lasth = $prev_lasth;
25260  }
25261 
25268  public function setLIsymbol($symbol='!') {
25269  // check for custom image symbol
25270  if (substr($symbol, 0, 4) == 'img|') {
25271  $this->lisymbol = $symbol;
25272  return;
25273  }
25274  $symbol = strtolower($symbol);
25275  switch ($symbol) {
25276  case '!' :
25277  case '#' :
25278  case 'disc' :
25279  case 'circle' :
25280  case 'square' :
25281  case '1':
25282  case 'decimal':
25283  case 'decimal-leading-zero':
25284  case 'i':
25285  case 'lower-roman':
25286  case 'I':
25287  case 'upper-roman':
25288  case 'a':
25289  case 'lower-alpha':
25290  case 'lower-latin':
25291  case 'A':
25292  case 'upper-alpha':
25293  case 'upper-latin':
25294  case 'lower-greek': {
25295  $this->lisymbol = $symbol;
25296  break;
25297  }
25298  default : {
25299  $this->lisymbol = '';
25300  }
25301  }
25302  }
25303 
25312  public function SetBooklet($booklet=true, $inner=-1, $outer=-1) {
25313  $this->booklet = $booklet;
25314  if ($inner >= 0) {
25315  $this->lMargin = $inner;
25316  }
25317  if ($outer >= 0) {
25318  $this->rMargin = $outer;
25319  }
25320  }
25321 
25328  protected function swapMargins($reverse=true) {
25329  if ($reverse) {
25330  // swap left and right margins
25331  $mtemp = $this->original_lMargin;
25332  $this->original_lMargin = $this->original_rMargin;
25333  $this->original_rMargin = $mtemp;
25334  $deltam = $this->original_lMargin - $this->original_rMargin;
25335  $this->lMargin += $deltam;
25336  $this->rMargin -= $deltam;
25337  }
25338  }
25339 
25352  public function setHtmlVSpace($tagvs) {
25353  $this->tagvspaces = $tagvs;
25354  }
25355 
25362  public function setListIndentWidth($width) {
25363  return $this->customlistindent = floatval($width);
25364  }
25365 
25372  public function setOpenCell($isopen) {
25373  $this->opencell = $isopen;
25374  }
25375 
25383  public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') {
25384  $this->htmlLinkColorArray = $color;
25385  $this->htmlLinkFontStyle = $fontstyle;
25386  }
25387 
25398  public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) {
25399  $supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt');
25400  $retval = 0;
25401  $value = 0;
25402  $unit = 'px';
25403  $k = $this->k;
25404  if ($points) {
25405  $k = 1;
25406  }
25407  if (in_array($defaultunit, $supportedunits)) {
25408  $unit = $defaultunit;
25409  }
25410  if (is_numeric($htmlval)) {
25411  $value = floatval($htmlval);
25412  } elseif (preg_match('/([0-9\.\-\+]+)/', $htmlval, $mnum)) {
25413  $value = floatval($mnum[1]);
25414  if (preg_match('/([a-z%]+)/', $htmlval, $munit)) {
25415  if (in_array($munit[1], $supportedunits)) {
25416  $unit = $munit[1];
25417  }
25418  }
25419  }
25420  switch ($unit) {
25421  // percentage
25422  case '%': {
25423  $retval = (($value * $refsize) / 100);
25424  break;
25425  }
25426  // relative-size
25427  case 'em': {
25428  $retval = ($value * $refsize);
25429  break;
25430  }
25431  // height of lower case 'x' (about half the font-size)
25432  case 'ex': {
25433  $retval = $value * ($refsize / 2);
25434  break;
25435  }
25436  // absolute-size
25437  case 'in': {
25438  $retval = ($value * $this->dpi) / $k;
25439  break;
25440  }
25441  // centimeters
25442  case 'cm': {
25443  $retval = ($value / 2.54 * $this->dpi) / $k;
25444  break;
25445  }
25446  // millimeters
25447  case 'mm': {
25448  $retval = ($value / 25.4 * $this->dpi) / $k;
25449  break;
25450  }
25451  // one pica is 12 points
25452  case 'pc': {
25453  $retval = ($value * 12) / $k;
25454  break;
25455  }
25456  // points
25457  case 'pt': {
25458  $retval = $value / $k;
25459  break;
25460  }
25461  // pixels
25462  case 'px': {
25463  $retval = $this->pixelsToUnits($value);
25464  break;
25465  }
25466  }
25467  return $retval;
25468  }
25469 
25477  public function intToRoman($number) {
25478  $roman = '';
25479  while ($number >= 1000) {
25480  $roman .= 'M';
25481  $number -= 1000;
25482  }
25483  while ($number >= 900) {
25484  $roman .= 'CM';
25485  $number -= 900;
25486  }
25487  while ($number >= 500) {
25488  $roman .= 'D';
25489  $number -= 500;
25490  }
25491  while ($number >= 400) {
25492  $roman .= 'CD';
25493  $number -= 400;
25494  }
25495  while ($number >= 100) {
25496  $roman .= 'C';
25497  $number -= 100;
25498  }
25499  while ($number >= 90) {
25500  $roman .= 'XC';
25501  $number -= 90;
25502  }
25503  while ($number >= 50) {
25504  $roman .= 'L';
25505  $number -= 50;
25506  }
25507  while ($number >= 40) {
25508  $roman .= 'XL';
25509  $number -= 40;
25510  }
25511  while ($number >= 10) {
25512  $roman .= 'X';
25513  $number -= 10;
25514  }
25515  while ($number >= 9) {
25516  $roman .= 'IX';
25517  $number -= 9;
25518  }
25519  while ($number >= 5) {
25520  $roman .= 'V';
25521  $number -= 5;
25522  }
25523  while ($number >= 4) {
25524  $roman .= 'IV';
25525  $number -= 4;
25526  }
25527  while ($number >= 1) {
25528  $roman .= 'I';
25529  --$number;
25530  }
25531  return $roman;
25532  }
25533 
25542  protected function putHtmlListBullet($listdepth, $listtype='', $size=10) {
25543  if ($this->state != 2) {
25544  return;
25545  }
25546  $size /= $this->k;
25547  $fill = '';
25548  $bgcolor = $this->bgcolor;
25549  $color = $this->fgcolor;
25550  $strokecolor = $this->strokecolor;
25551  $width = 0;
25552  $textitem = '';
25553  $tmpx = $this->x;
25554  $lspace = $this->GetStringWidth(' ');
25555  if ($listtype == '^') {
25556  // special symbol used for avoid justification of rect bullet
25557  $this->lispacer = '';
25558  return;
25559  } elseif ($listtype == '!') {
25560  // set default list type for unordered list
25561  $deftypes = array('disc', 'circle', 'square');
25562  $listtype = $deftypes[($listdepth - 1) % 3];
25563  } elseif ($listtype == '#') {
25564  // set default list type for ordered list
25565  $listtype = 'decimal';
25566  } elseif (substr($listtype, 0, 4) == 'img|') {
25567  // custom image type ('img|type|width|height|image.ext')
25568  $img = explode('|', $listtype);
25569  $listtype = 'img';
25570  }
25571  switch ($listtype) {
25572  // unordered types
25573  case 'none': {
25574  break;
25575  }
25576  case 'disc': {
25577  $r = $size / 6;
25578  $lspace += (2 * $r);
25579  if ($this->rtl) {
25580  $this->x += $lspace;
25581  } else {
25582  $this->x -= $lspace;
25583  }
25584  $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), $r, 0, 360, 'F', array(), $color, 8);
25585  break;
25586  }
25587  case 'circle': {
25588  $r = $size / 6;
25589  $lspace += (2 * $r);
25590  if ($this->rtl) {
25591  $this->x += $lspace;
25592  } else {
25593  $this->x -= $lspace;
25594  }
25595  $prev_line_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor;
25596  $new_line_style = array('width' => ($r / 3), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'phase' => 0, 'color'=>$color);
25597  $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), ($r * (1 - (1/6))), 0, 360, 'D', $new_line_style, array(), 8);
25598  $this->_out($prev_line_style); // restore line settings
25599  break;
25600  }
25601  case 'square': {
25602  $l = $size / 3;
25603  $lspace += $l;
25604  if ($this->rtl) {;
25605  $this->x += $lspace;
25606  } else {
25607  $this->x -= $lspace;
25608  }
25609  $this->Rect($this->x, ($this->y + (($this->lasth - $l) / 2)), $l, $l, 'F', array(), $color);
25610  break;
25611  }
25612  case 'img': {
25613  // 1=>type, 2=>width, 3=>height, 4=>image.ext
25614  $lspace += $img[2];
25615  if ($this->rtl) {;
25616  $this->x += $lspace;
25617  } else {
25618  $this->x -= $lspace;
25619  }
25620  $imgtype = strtolower($img[1]);
25621  $prev_y = $this->y;
25622  switch ($imgtype) {
25623  case 'svg': {
25624  $this->ImageSVG($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', 'T', '', 0, false);
25625  break;
25626  }
25627  case 'ai':
25628  case 'eps': {
25629  $this->ImageEps($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', true, 'T', '', 0, false);
25630  break;
25631  }
25632  default: {
25633  $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);
25634  break;
25635  }
25636  }
25637  $this->y = $prev_y;
25638  break;
25639  }
25640  // ordered types
25641  // $this->listcount[$this->listnum];
25642  // $textitem
25643  case '1':
25644  case 'decimal': {
25645  $textitem = $this->listcount[$this->listnum];
25646  break;
25647  }
25648  case 'decimal-leading-zero': {
25649  $textitem = sprintf('%02d', $this->listcount[$this->listnum]);
25650  break;
25651  }
25652  case 'i':
25653  case 'lower-roman': {
25654  $textitem = strtolower($this->intToRoman($this->listcount[$this->listnum]));
25655  break;
25656  }
25657  case 'I':
25658  case 'upper-roman': {
25659  $textitem = $this->intToRoman($this->listcount[$this->listnum]);
25660  break;
25661  }
25662  case 'a':
25663  case 'lower-alpha':
25664  case 'lower-latin': {
25665  $textitem = chr(97 + $this->listcount[$this->listnum] - 1);
25666  break;
25667  }
25668  case 'A':
25669  case 'upper-alpha':
25670  case 'upper-latin': {
25671  $textitem = chr(65 + $this->listcount[$this->listnum] - 1);
25672  break;
25673  }
25674  case 'lower-greek': {
25675  $textitem = $this->unichr(945 + $this->listcount[$this->listnum] - 1);
25676  break;
25677  }
25678  /*
25679  // Types to be implemented (special handling)
25680  case 'hebrew': {
25681  break;
25682  }
25683  case 'armenian': {
25684  break;
25685  }
25686  case 'georgian': {
25687  break;
25688  }
25689  case 'cjk-ideographic': {
25690  break;
25691  }
25692  case 'hiragana': {
25693  break;
25694  }
25695  case 'katakana': {
25696  break;
25697  }
25698  case 'hiragana-iroha': {
25699  break;
25700  }
25701  case 'katakana-iroha': {
25702  break;
25703  }
25704  */
25705  default: {
25706  $textitem = $this->listcount[$this->listnum];
25707  }
25708  }
25709  if (!$this->empty_string($textitem)) {
25710  // Check whether we need a new page or new column
25711  $prev_y = $this->y;
25712  $h = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
25713  if ($this->checkPageBreak($h) OR ($this->y < $prev_y)) {
25714  $tmpx = $this->x;
25715  }
25716  // print ordered item
25717  if ($this->rtl) {
25718  $textitem = '.'.$textitem;
25719  } else {
25720  $textitem = $textitem.'.';
25721  }
25722  $lspace += $this->GetStringWidth($textitem);
25723  if ($this->rtl) {
25724  $this->x += $lspace;
25725  } else {
25726  $this->x -= $lspace;
25727  }
25728  $this->Write($this->lasth, $textitem, '', false, '', false, 0, false);
25729  }
25730  $this->x = $tmpx;
25731  $this->lispacer = '^';
25732  // restore colors
25733  $this->SetFillColorArray($bgcolor);
25734  $this->SetDrawColorArray($strokecolor);
25735  $this->SettextColorArray($color);
25736  }
25737 
25744  protected function getGraphicVars() {
25745  $grapvars = array(
25746  'FontFamily' => $this->FontFamily,
25747  'FontStyle' => $this->FontStyle,
25748  'FontSizePt' => $this->FontSizePt,
25749  'rMargin' => $this->rMargin,
25750  'lMargin' => $this->lMargin,
25751  'cell_padding' => $this->cell_padding,
25752  'cell_margin' => $this->cell_margin,
25753  'LineWidth' => $this->LineWidth,
25754  'linestyleWidth' => $this->linestyleWidth,
25755  'linestyleCap' => $this->linestyleCap,
25756  'linestyleJoin' => $this->linestyleJoin,
25757  'linestyleDash' => $this->linestyleDash,
25758  'textrendermode' => $this->textrendermode,
25759  'textstrokewidth' => $this->textstrokewidth,
25760  'DrawColor' => $this->DrawColor,
25761  'FillColor' => $this->FillColor,
25762  'TextColor' => $this->TextColor,
25763  'ColorFlag' => $this->ColorFlag,
25764  'bgcolor' => $this->bgcolor,
25765  'fgcolor' => $this->fgcolor,
25766  'htmlvspace' => $this->htmlvspace,
25767  'listindent' => $this->listindent,
25768  'listindentlevel' => $this->listindentlevel,
25769  'listnum' => $this->listnum,
25770  'listordered' => $this->listordered,
25771  'listcount' => $this->listcount,
25772  'lispacer' => $this->lispacer,
25773  'cell_height_ratio' => $this->cell_height_ratio,
25774  'font_stretching' => $this->font_stretching,
25775  'font_spacing' => $this->font_spacing,
25776  'alpha' => $this->alpha,
25777  // extended
25778  'lasth' => $this->lasth,
25779  'tMargin' => $this->tMargin,
25780  'bMargin' => $this->bMargin,
25781  'AutoPageBreak' => $this->AutoPageBreak,
25782  'PageBreakTrigger' => $this->PageBreakTrigger,
25783  'x' => $this->x,
25784  'y' => $this->y,
25785  'w' => $this->w,
25786  'h' => $this->h,
25787  'wPt' => $this->wPt,
25788  'hPt' => $this->hPt,
25789  'fwPt' => $this->fwPt,
25790  'fhPt' => $this->fhPt,
25791  'page' => $this->page,
25792  'current_column' => $this->current_column,
25793  'num_columns' => $this->num_columns
25794  );
25795  return $grapvars;
25796  }
25797 
25805  protected function setGraphicVars($gvars, $extended=false) {
25806  if ($this->state != 2) {
25807  return;
25808  }
25809  $this->FontFamily = $gvars['FontFamily'];
25810  $this->FontStyle = $gvars['FontStyle'];
25811  $this->FontSizePt = $gvars['FontSizePt'];
25812  $this->rMargin = $gvars['rMargin'];
25813  $this->lMargin = $gvars['lMargin'];
25814  $this->cell_padding = $gvars['cell_padding'];
25815  $this->cell_margin = $gvars['cell_margin'];
25816  $this->LineWidth = $gvars['LineWidth'];
25817  $this->linestyleWidth = $gvars['linestyleWidth'];
25818  $this->linestyleCap = $gvars['linestyleCap'];
25819  $this->linestyleJoin = $gvars['linestyleJoin'];
25820  $this->linestyleDash = $gvars['linestyleDash'];
25821  $this->textrendermode = $gvars['textrendermode'];
25822  $this->textstrokewidth = $gvars['textstrokewidth'];
25823  $this->DrawColor = $gvars['DrawColor'];
25824  $this->FillColor = $gvars['FillColor'];
25825  $this->TextColor = $gvars['TextColor'];
25826  $this->ColorFlag = $gvars['ColorFlag'];
25827  $this->bgcolor = $gvars['bgcolor'];
25828  $this->fgcolor = $gvars['fgcolor'];
25829  $this->htmlvspace = $gvars['htmlvspace'];
25830  $this->listindent = $gvars['listindent'];
25831  $this->listindentlevel = $gvars['listindentlevel'];
25832  $this->listnum = $gvars['listnum'];
25833  $this->listordered = $gvars['listordered'];
25834  $this->listcount = $gvars['listcount'];
25835  $this->lispacer = $gvars['lispacer'];
25836  $this->cell_height_ratio = $gvars['cell_height_ratio'];
25837  $this->font_stretching = $gvars['font_stretching'];
25838  $this->font_spacing = $gvars['font_spacing'];
25839  $this->alpha = $gvars['alpha'];
25840  if ($extended) {
25841  // restore extended values
25842  $this->lasth = $gvars['lasth'];
25843  $this->tMargin = $gvars['tMargin'];
25844  $this->bMargin = $gvars['bMargin'];
25845  $this->AutoPageBreak = $gvars['AutoPageBreak'];
25846  $this->PageBreakTrigger = $gvars['PageBreakTrigger'];
25847  $this->x = $gvars['x'];
25848  $this->y = $gvars['y'];
25849  $this->w = $gvars['w'];
25850  $this->h = $gvars['h'];
25851  $this->wPt = $gvars['wPt'];
25852  $this->hPt = $gvars['hPt'];
25853  $this->fwPt = $gvars['fwPt'];
25854  $this->fhPt = $gvars['fhPt'];
25855  $this->page = $gvars['page'];
25856  $this->current_column = $gvars['current_column'];
25857  $this->num_columns = $gvars['num_columns'];
25858  }
25859  $this->_out(''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor.'');
25860  if (!$this->empty_string($this->FontFamily)) {
25861  $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
25862  }
25863  }
25864 
25872  protected function getObjFilename($name) {
25873  return tempnam(K_PATH_CACHE, $name.'_');
25874  }
25875 
25884  protected function writeDiskCache($filename, $data, $append=false) {
25885  if ($append) {
25886  $fmode = 'ab+';
25887  } else {
25888  $fmode = 'wb+';
25889  }
25890  $f = @fopen($filename, $fmode);
25891  if (!$f) {
25892  $this->Error('Unable to write cache file: '.$filename);
25893  } else {
25894  fwrite($f, $data);
25895  fclose($f);
25896  }
25897  // update file length (needed for transactions)
25898  if (!isset($this->cache_file_length['_'.$filename])) {
25899  $this->cache_file_length['_'.$filename] = strlen($data);
25900  } else {
25901  $this->cache_file_length['_'.$filename] += strlen($data);
25902  }
25903  }
25904 
25912  protected function readDiskCache($filename) {
25913  return file_get_contents($filename);
25914  }
25915 
25922  protected function setBuffer($data) {
25923  $this->bufferlen += strlen($data);
25924  if ($this->diskcache) {
25925  if (!isset($this->buffer) OR $this->empty_string($this->buffer)) {
25926  $this->buffer = $this->getObjFilename('buffer');
25927  }
25928  $this->writeDiskCache($this->buffer, $data, true);
25929  } else {
25930  $this->buffer .= $data;
25931  }
25932  }
25933 
25940  protected function replaceBuffer($data) {
25941  $this->bufferlen = strlen($data);
25942  if ($this->diskcache) {
25943  if (!isset($this->buffer) OR $this->empty_string($this->buffer)) {
25944  $this->buffer = $this->getObjFilename('buffer');
25945  }
25946  $this->writeDiskCache($this->buffer, $data, false);
25947  } else {
25948  $this->buffer = $data;
25949  }
25950  }
25951 
25958  protected function getBuffer() {
25959  if ($this->diskcache) {
25960  return $this->readDiskCache($this->buffer);
25961  } else {
25962  return $this->buffer;
25963  }
25964  }
25965 
25974  protected function setPageBuffer($page, $data, $append=false) {
25975  if ($this->diskcache) {
25976  if (!isset($this->pages[$page])) {
25977  $this->pages[$page] = $this->getObjFilename('page'.$page);
25978  }
25979  $this->writeDiskCache($this->pages[$page], $data, $append);
25980  } else {
25981  if ($append) {
25982  $this->pages[$page] .= $data;
25983  } else {
25984  $this->pages[$page] = $data;
25985  }
25986  }
25987  if ($append AND isset($this->pagelen[$page])) {
25988  $this->pagelen[$page] += strlen($data);
25989  } else {
25990  $this->pagelen[$page] = strlen($data);
25991  }
25992  }
25993 
26001  protected function getPageBuffer($page) {
26002  if ($this->diskcache) {
26003  return $this->readDiskCache($this->pages[$page]);
26004  } elseif (isset($this->pages[$page])) {
26005  return $this->pages[$page];
26006  }
26007  return false;
26008  }
26009 
26018  protected function setImageBuffer($image, $data) {
26019  if (($data['i'] = array_search($image, $this->imagekeys)) === FALSE) {
26020  $this->imagekeys[$this->numimages] = $image;
26021  $data['i'] = $this->numimages;
26022  ++$this->numimages;
26023  }
26024  if ($this->diskcache) {
26025  if (!isset($this->images[$image])) {
26026  $this->images[$image] = $this->getObjFilename('image'.$image);
26027  }
26028  $this->writeDiskCache($this->images[$image], serialize($data));
26029  } else {
26030  $this->images[$image] = $data;
26031  }
26032  return $data['i'];
26033  }
26034 
26043  protected function setImageSubBuffer($image, $key, $data) {
26044  if (!isset($this->images[$image])) {
26045  $this->setImageBuffer($image, array());
26046  }
26047  if ($this->diskcache) {
26048  $tmpimg = $this->getImageBuffer($image);
26049  $tmpimg[$key] = $data;
26050  $this->writeDiskCache($this->images[$image], serialize($tmpimg));
26051  } else {
26052  $this->images[$image][$key] = $data;
26053  }
26054  }
26055 
26063  protected function getImageBuffer($image) {
26064  if ($this->diskcache AND isset($this->images[$image])) {
26065  return unserialize($this->readDiskCache($this->images[$image]));
26066  } elseif (isset($this->images[$image])) {
26067  return $this->images[$image];
26068  }
26069  return false;
26070  }
26071 
26079  protected function setFontBuffer($font, $data) {
26080  if ($this->diskcache) {
26081  if (!isset($this->fonts[$font])) {
26082  $this->fonts[$font] = $this->getObjFilename('font');
26083  }
26084  $this->writeDiskCache($this->fonts[$font], serialize($data));
26085  } else {
26086  $this->fonts[$font] = $data;
26087  }
26088  if (!in_array($font, $this->fontkeys)) {
26089  $this->fontkeys[] = $font;
26090  // store object ID for current font
26091  ++$this->n;
26092  $this->font_obj_ids[$font] = $this->n;
26093  $this->setFontSubBuffer($font, 'n', $this->n);
26094  }
26095  }
26096 
26105  protected function setFontSubBuffer($font, $key, $data) {
26106  if (!isset($this->fonts[$font])) {
26107  $this->setFontBuffer($font, array());
26108  }
26109  if ($this->diskcache) {
26110  $tmpfont = $this->getFontBuffer($font);
26111  $tmpfont[$key] = $data;
26112  $this->writeDiskCache($this->fonts[$font], serialize($tmpfont));
26113  } else {
26114  $this->fonts[$font][$key] = $data;
26115  }
26116  }
26117 
26125  protected function getFontBuffer($font) {
26126  if ($this->diskcache AND isset($this->fonts[$font])) {
26127  return unserialize($this->readDiskCache($this->fonts[$font]));
26128  } elseif (isset($this->fonts[$font])) {
26129  return $this->fonts[$font];
26130  }
26131  return false;
26132  }
26133 
26142  public function movePage($frompage, $topage) {
26143  if (($frompage > $this->numpages) OR ($frompage <= $topage)) {
26144  return false;
26145  }
26146  if ($frompage == $this->page) {
26147  // close the page before moving it
26148  $this->endPage();
26149  }
26150  // move all page-related states
26151  $tmppage = $this->getPageBuffer($frompage);
26152  $tmppagedim = $this->pagedim[$frompage];
26153  $tmppagelen = $this->pagelen[$frompage];
26154  $tmpintmrk = $this->intmrk[$frompage];
26155  $tmpbordermrk = $this->bordermrk[$frompage];
26156  $tmpcntmrk = $this->cntmrk[$frompage];
26157  $tmppageobjects = $this->pageobjects[$frompage];
26158  if (isset($this->footerpos[$frompage])) {
26159  $tmpfooterpos = $this->footerpos[$frompage];
26160  }
26161  if (isset($this->footerlen[$frompage])) {
26162  $tmpfooterlen = $this->footerlen[$frompage];
26163  }
26164  if (isset($this->transfmrk[$frompage])) {
26165  $tmptransfmrk = $this->transfmrk[$frompage];
26166  }
26167  if (isset($this->PageAnnots[$frompage])) {
26168  $tmpannots = $this->PageAnnots[$frompage];
26169  }
26170  if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) {
26171  for ($i = $frompage; $i > $topage; --$i) {
26172  if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $frompage)) {
26173  --$this->pagegroups[$this->newpagegroup[$i]];
26174  break;
26175  }
26176  }
26177  for ($i = $topage; $i > 0; --$i) {
26178  if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $topage)) {
26179  ++$this->pagegroups[$this->newpagegroup[$i]];
26180  break;
26181  }
26182  }
26183  }
26184  for ($i = $frompage; $i > $topage; --$i) {
26185  $j = $i - 1;
26186  // shift pages down
26187  $this->setPageBuffer($i, $this->getPageBuffer($j));
26188  $this->pagedim[$i] = $this->pagedim[$j];
26189  $this->pagelen[$i] = $this->pagelen[$j];
26190  $this->intmrk[$i] = $this->intmrk[$j];
26191  $this->bordermrk[$i] = $this->bordermrk[$j];
26192  $this->cntmrk[$i] = $this->cntmrk[$j];
26193  $this->pageobjects[$i] = $this->pageobjects[$j];
26194  if (isset($this->footerpos[$j])) {
26195  $this->footerpos[$i] = $this->footerpos[$j];
26196  } elseif (isset($this->footerpos[$i])) {
26197  unset($this->footerpos[$i]);
26198  }
26199  if (isset($this->footerlen[$j])) {
26200  $this->footerlen[$i] = $this->footerlen[$j];
26201  } elseif (isset($this->footerlen[$i])) {
26202  unset($this->footerlen[$i]);
26203  }
26204  if (isset($this->transfmrk[$j])) {
26205  $this->transfmrk[$i] = $this->transfmrk[$j];
26206  } elseif (isset($this->transfmrk[$i])) {
26207  unset($this->transfmrk[$i]);
26208  }
26209  if (isset($this->PageAnnots[$j])) {
26210  $this->PageAnnots[$i] = $this->PageAnnots[$j];
26211  } elseif (isset($this->PageAnnots[$i])) {
26212  unset($this->PageAnnots[$i]);
26213  }
26214  if (isset($this->newpagegroup[$j])) {
26215  $this->newpagegroup[$i] = $this->newpagegroup[$j];
26216  unset($this->newpagegroup[$j]);
26217  }
26218  if ($this->currpagegroup == $j) {
26219  $this->currpagegroup = $i;
26220  }
26221  }
26222  $this->setPageBuffer($topage, $tmppage);
26223  $this->pagedim[$topage] = $tmppagedim;
26224  $this->pagelen[$topage] = $tmppagelen;
26225  $this->intmrk[$topage] = $tmpintmrk;
26226  $this->bordermrk[$topage] = $tmpbordermrk;
26227  $this->cntmrk[$topage] = $tmpcntmrk;
26228  $this->pageobjects[$topage] = $tmppageobjects;
26229  if (isset($tmpfooterpos)) {
26230  $this->footerpos[$topage] = $tmpfooterpos;
26231  } elseif (isset($this->footerpos[$topage])) {
26232  unset($this->footerpos[$topage]);
26233  }
26234  if (isset($tmpfooterlen)) {
26235  $this->footerlen[$topage] = $tmpfooterlen;
26236  } elseif (isset($this->footerlen[$topage])) {
26237  unset($this->footerlen[$topage]);
26238  }
26239  if (isset($tmptransfmrk)) {
26240  $this->transfmrk[$topage] = $tmptransfmrk;
26241  } elseif (isset($this->transfmrk[$topage])) {
26242  unset($this->transfmrk[$topage]);
26243  }
26244  if (isset($tmpannots)) {
26245  $this->PageAnnots[$topage] = $tmpannots;
26246  } elseif (isset($this->PageAnnots[$topage])) {
26247  unset($this->PageAnnots[$topage]);
26248  }
26249  // adjust outlines
26250  $tmpoutlines = $this->outlines;
26251  foreach ($tmpoutlines as $key => $outline) {
26252  if (($outline['p'] >= $topage) AND ($outline['p'] < $frompage)) {
26253  $this->outlines[$key]['p'] = ($outline['p'] + 1);
26254  } elseif ($outline['p'] == $frompage) {
26255  $this->outlines[$key]['p'] = $topage;
26256  }
26257  }
26258  // adjust dests
26259  $tmpdests = $this->dests;
26260  foreach ($tmpdests as $key => $dest) {
26261  if (($dest['p'] >= $topage) AND ($dest['p'] < $frompage)) {
26262  $this->dests[$key]['p'] = ($dest['p'] + 1);
26263  } elseif ($dest['p'] == $frompage) {
26264  $this->dests[$key]['p'] = $topage;
26265  }
26266  }
26267  // adjust links
26268  $tmplinks = $this->links;
26269  foreach ($tmplinks as $key => $link) {
26270  if (($link[0] >= $topage) AND ($link[0] < $frompage)) {
26271  $this->links[$key][0] = ($link[0] + 1);
26272  } elseif ($link[0] == $frompage) {
26273  $this->links[$key][0] = $topage;
26274  }
26275  }
26276  // adjust javascript
26277  $tmpjavascript = $this->javascript;
26278  global $jfrompage, $jtopage;
26279  $jfrompage = $frompage;
26280  $jtopage = $topage;
26281  $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/',
26282  create_function('$matches', 'global $jfrompage, $jtopage;
26283  $pagenum = intval($matches[3]) + 1;
26284  if (($pagenum >= $jtopage) AND ($pagenum < $jfrompage)) {
26285  $newpage = ($pagenum + 1);
26286  } elseif ($pagenum == $jfrompage) {
26287  $newpage = $jtopage;
26288  } else {
26289  $newpage = $pagenum;
26290  }
26291  --$newpage;
26292  return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript);
26293  // return to last page
26294  $this->lastPage(true);
26295  return true;
26296  }
26297 
26305  public function deletePage($page) {
26306  if (($page < 1) OR ($page > $this->numpages)) {
26307  return false;
26308  }
26309  // delete current page
26310  unset($this->pages[$page]);
26311  unset($this->pagedim[$page]);
26312  unset($this->pagelen[$page]);
26313  unset($this->intmrk[$page]);
26314  unset($this->bordermrk[$page]);
26315  unset($this->cntmrk[$page]);
26316  foreach ($this->pageobjects[$page] as $oid) {
26317  if (isset($this->offsets[$oid])){
26318  unset($this->offsets[$oid]);
26319  }
26320  }
26321  unset($this->pageobjects[$page]);
26322  if (isset($this->footerpos[$page])) {
26323  unset($this->footerpos[$page]);
26324  }
26325  if (isset($this->footerlen[$page])) {
26326  unset($this->footerlen[$page]);
26327  }
26328  if (isset($this->transfmrk[$page])) {
26329  unset($this->transfmrk[$page]);
26330  }
26331  if (isset($this->PageAnnots[$page])) {
26332  unset($this->PageAnnots[$page]);
26333  }
26334  if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) {
26335  for ($i = $page; $i > 0; --$i) {
26336  if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $page)) {
26337  --$this->pagegroups[$this->newpagegroup[$i]];
26338  break;
26339  }
26340  }
26341  }
26342  if (isset($this->pageopen[$page])) {
26343  unset($this->pageopen[$page]);
26344  }
26345  if ($page < $this->numpages) {
26346  // update remaining pages
26347  for ($i = $page; $i < $this->numpages; ++$i) {
26348  $j = $i + 1;
26349  // shift pages
26350  $this->setPageBuffer($i, $this->getPageBuffer($j));
26351  $this->pagedim[$i] = $this->pagedim[$j];
26352  $this->pagelen[$i] = $this->pagelen[$j];
26353  $this->intmrk[$i] = $this->intmrk[$j];
26354  $this->bordermrk[$i] = $this->bordermrk[$j];
26355  $this->cntmrk[$i] = $this->cntmrk[$j];
26356  $this->pageobjects[$i] = $this->pageobjects[$j];
26357  if (isset($this->footerpos[$j])) {
26358  $this->footerpos[$i] = $this->footerpos[$j];
26359  } elseif (isset($this->footerpos[$i])) {
26360  unset($this->footerpos[$i]);
26361  }
26362  if (isset($this->footerlen[$j])) {
26363  $this->footerlen[$i] = $this->footerlen[$j];
26364  } elseif (isset($this->footerlen[$i])) {
26365  unset($this->footerlen[$i]);
26366  }
26367  if (isset($this->transfmrk[$j])) {
26368  $this->transfmrk[$i] = $this->transfmrk[$j];
26369  } elseif (isset($this->transfmrk[$i])) {
26370  unset($this->transfmrk[$i]);
26371  }
26372  if (isset($this->PageAnnots[$j])) {
26373  $this->PageAnnots[$i] = $this->PageAnnots[$j];
26374  } elseif (isset($this->PageAnnots[$i])) {
26375  unset($this->PageAnnots[$i]);
26376  }
26377  if (isset($this->newpagegroup[$j])) {
26378  $this->newpagegroup[$i] = $this->newpagegroup[$j];
26379  unset($this->newpagegroup[$j]);
26380  }
26381  if ($this->currpagegroup == $j) {
26382  $this->currpagegroup = $i;
26383  }
26384  if (isset($this->pageopen[$j])) {
26385  $this->pageopen[$i] = $this->pageopen[$j];
26386  } elseif (isset($this->pageopen[$i])) {
26387  unset($this->pageopen[$i]);
26388  }
26389  }
26390  // remove last page
26391  unset($this->pages[$this->numpages]);
26392  unset($this->pagedim[$this->numpages]);
26393  unset($this->pagelen[$this->numpages]);
26394  unset($this->intmrk[$this->numpages]);
26395  unset($this->bordermrk[$this->numpages]);
26396  unset($this->cntmrk[$this->numpages]);
26397  foreach ($this->pageobjects[$this->numpages] as $oid) {
26398  if (isset($this->offsets[$oid])){
26399  unset($this->offsets[$oid]);
26400  }
26401  }
26402  unset($this->pageobjects[$this->numpages]);
26403  if (isset($this->footerpos[$this->numpages])) {
26404  unset($this->footerpos[$this->numpages]);
26405  }
26406  if (isset($this->footerlen[$this->numpages])) {
26407  unset($this->footerlen[$this->numpages]);
26408  }
26409  if (isset($this->transfmrk[$this->numpages])) {
26410  unset($this->transfmrk[$this->numpages]);
26411  }
26412  if (isset($this->PageAnnots[$this->numpages])) {
26413  unset($this->PageAnnots[$this->numpages]);
26414  }
26415  if (isset($this->newpagegroup[$this->numpages])) {
26416  unset($this->newpagegroup[$this->numpages]);
26417  }
26418  if ($this->currpagegroup == $this->numpages) {
26419  $this->currpagegroup = ($this->numpages - 1);
26420  }
26421  if (isset($this->pagegroups[$this->numpages])) {
26422  unset($this->pagegroups[$this->numpages]);
26423  }
26424  if (isset($this->pageopen[$this->numpages])) {
26425  unset($this->pageopen[$this->numpages]);
26426  }
26427  }
26428  --$this->numpages;
26429  $this->page = $this->numpages;
26430  // adjust outlines
26431  $tmpoutlines = $this->outlines;
26432  foreach ($tmpoutlines as $key => $outline) {
26433  if ($outline['p'] > $page) {
26434  $this->outlines[$key]['p'] = $outline['p'] - 1;
26435  } elseif ($outline['p'] == $page) {
26436  unset($this->outlines[$key]);
26437  }
26438  }
26439  // adjust dests
26440  $tmpdests = $this->dests;
26441  foreach ($tmpdests as $key => $dest) {
26442  if ($dest['p'] > $page) {
26443  $this->dests[$key]['p'] = $dest['p'] - 1;
26444  } elseif ($dest['p'] == $page) {
26445  unset($this->dests[$key]);
26446  }
26447  }
26448  // adjust links
26449  $tmplinks = $this->links;
26450  foreach ($tmplinks as $key => $link) {
26451  if ($link[0] > $page) {
26452  $this->links[$key][0] = $link[0] - 1;
26453  } elseif ($link[0] == $page) {
26454  unset($this->links[$key]);
26455  }
26456  }
26457  // adjust javascript
26458  $tmpjavascript = $this->javascript;
26459  global $jpage;
26460  $jpage = $page;
26461  $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/',
26462  create_function('$matches', 'global $jpage;
26463  $pagenum = intval($matches[3]) + 1;
26464  if ($pagenum >= $jpage) {
26465  $newpage = ($pagenum - 1);
26466  } elseif ($pagenum == $jpage) {
26467  $newpage = 1;
26468  } else {
26469  $newpage = $pagenum;
26470  }
26471  --$newpage;
26472  return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript);
26473  // return to last page
26474  $this->lastPage(true);
26475  return true;
26476  }
26477 
26485  public function copyPage($page=0) {
26486  if ($page == 0) {
26487  // default value
26488  $page = $this->page;
26489  }
26490  if (($page < 1) OR ($page > $this->numpages)) {
26491  return false;
26492  }
26493  // close the last page
26494  $this->endPage();
26495  // copy all page-related states
26496  ++$this->numpages;
26497  $this->page = $this->numpages;
26498  $this->setPageBuffer($this->page, $this->getPageBuffer($page));
26499  $this->pagedim[$this->page] = $this->pagedim[$page];
26500  $this->pagelen[$this->page] = $this->pagelen[$page];
26501  $this->intmrk[$this->page] = $this->intmrk[$page];
26502  $this->bordermrk[$this->page] = $this->bordermrk[$page];
26503  $this->cntmrk[$this->page] = $this->cntmrk[$page];
26504  $this->pageobjects[$this->page] = $this->pageobjects[$page];
26505  $this->pageopen[$this->page] = false;
26506  if (isset($this->footerpos[$page])) {
26507  $this->footerpos[$this->page] = $this->footerpos[$page];
26508  }
26509  if (isset($this->footerlen[$page])) {
26510  $this->footerlen[$this->page] = $this->footerlen[$page];
26511  }
26512  if (isset($this->transfmrk[$page])) {
26513  $this->transfmrk[$this->page] = $this->transfmrk[$page];
26514  }
26515  if (isset($this->PageAnnots[$page])) {
26516  $this->PageAnnots[$this->page] = $this->PageAnnots[$page];
26517  }
26518  if (isset($this->newpagegroup[$page])) {
26519  // start a new group
26520  $this->newpagegroup[$this->page] = sizeof($this->newpagegroup) + 1;
26521  $this->currpagegroup = $this->newpagegroup[$this->page];
26522  $this->pagegroups[$this->currpagegroup] = 1;
26523  } elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) {
26524  ++$this->pagegroups[$this->currpagegroup];
26525  }
26526  // copy outlines
26527  $tmpoutlines = $this->outlines;
26528  foreach ($tmpoutlines as $key => $outline) {
26529  if ($outline['p'] == $page) {
26530  $this->outlines[] = array('t' => $outline['t'], 'l' => $outline['l'], 'x' => $outline['x'], 'y' => $outline['y'], 'p' => $this->page, 's' => $outline['s'], 'c' => $outline['c']);
26531  }
26532  }
26533  // copy links
26534  $tmplinks = $this->links;
26535  foreach ($tmplinks as $key => $link) {
26536  if ($link[0] == $page) {
26537  $this->links[] = array($this->page, $link[1]);
26538  }
26539  }
26540  // return to last page
26541  $this->lastPage(true);
26542  return true;
26543  }
26544 
26562  public function addTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC', $style='', $color=array(0,0,0)) {
26563  $fontsize = $this->FontSizePt;
26564  $fontfamily = $this->FontFamily;
26565  $fontstyle = $this->FontStyle;
26566  $w = $this->w - $this->lMargin - $this->rMargin;
26567  $spacer = $this->GetStringWidth(chr(32)) * 4;
26568  $lmargin = $this->lMargin;
26569  $rmargin = $this->rMargin;
26570  $x_start = $this->GetX();
26571  $page_first = $this->page;
26572  $current_page = $this->page;
26573  $page_fill_start = false;
26574  $page_fill_end = false;
26575  $current_column = $this->current_column;
26576  if ($this->empty_string($numbersfont)) {
26577  $numbersfont = $this->default_monospaced_font;
26578  }
26579  if ($this->empty_string($filler)) {
26580  $filler = ' ';
26581  }
26582  if ($this->empty_string($page)) {
26583  $gap = ' ';
26584  } else {
26585  $gap = '';
26586  if ($page < 1) {
26587  $page = 1;
26588  }
26589  }
26590  $this->SetFont($numbersfont, $fontstyle, $fontsize);
26591  $numwidth = $this->GetStringWidth('00000');
26592  $maxpage = 0; //used for pages on attached documents
26593  foreach ($this->outlines as $key => $outline) {
26594  // check for extra pages (used for attachments)
26595  if (($this->page > $page_first) AND ($outline['p'] >= $this->numpages)) {
26596  $outline['p'] += ($this->page - $page_first);
26597  }
26598  if ($this->rtl) {
26599  $aligntext = 'R';
26600  $alignnum = 'L';
26601  } else {
26602  $aligntext = 'L';
26603  $alignnum = 'R';
26604  }
26605  if ($outline['l'] == 0) {
26606  $this->SetFont($fontfamily, $outline['s'].'B', $fontsize);
26607  } else {
26608  $this->SetFont($fontfamily, $outline['s'], $fontsize - $outline['l']);
26609  }
26610  $this->SetTextColorArray($outline['c']);
26611  // check for page break
26612  $this->checkPageBreak((2 * $this->FontSize * $this->cell_height_ratio));
26613  // set margins and X position
26614  if (($this->page == $current_page) AND ($this->current_column == $current_column)) {
26615  $this->lMargin = $lmargin;
26616  $this->rMargin = $rmargin;
26617  } else {
26618  if ($this->current_column != $current_column) {
26619  if ($this->rtl) {
26620  $x_start = $this->w - $this->columns[$this->current_column]['x'];
26621  } else {
26622  $x_start = $this->columns[$this->current_column]['x'];
26623  }
26624  }
26625  $lmargin = $this->lMargin;
26626  $rmargin = $this->rMargin;
26627  $current_page = $this->page;
26628  $current_column = $this->current_column;
26629  }
26630  $this->SetX($x_start);
26631  $indent = ($spacer * $outline['l']);
26632  if ($this->rtl) {
26633  $this->x -= $indent;
26634  $this->rMargin = $this->w - $this->x;
26635  } else {
26636  $this->x += $indent;
26637  $this->lMargin = $this->x;
26638  }
26639  $link = $this->AddLink();
26640  $this->SetLink($link, $outline['y'], $outline['p']);
26641  // write the text
26642  if ($this->rtl) {
26643  $txt = ' '.$outline['t'];
26644  } else {
26645  $txt = $outline['t'].' ';
26646  }
26647  $this->Write(0, $txt, $link, false, $aligntext, false, 0, false, false, 0, $numwidth, '');
26648  if ($this->rtl) {
26649  $tw = $this->x - $this->lMargin;
26650  } else {
26651  $tw = $this->w - $this->rMargin - $this->x;
26652  }
26653  $this->SetFont($numbersfont, $fontstyle, $fontsize);
26654  if ($this->empty_string($page)) {
26655  $pagenum = $outline['p'];
26656  } else {
26657  // placemark to be replaced with the correct number
26658  $pagenum = '{#'.($outline['p']).'}';
26659  if ($this->isUnicodeFont()) {
26660  $pagenum = '{'.$pagenum.'}';
26661  }
26662  $maxpage = max($maxpage, $outline['p']);
26663  }
26664  $fw = ($tw - $this->GetStringWidth($pagenum.$filler));
26665  $numfills = floor($fw / $this->GetStringWidth($filler));
26666  if ($numfills > 0) {
26667  $rowfill = str_repeat($filler, $numfills);
26668  } else {
26669  $rowfill = '';
26670  }
26671  if ($this->rtl) {
26672  $pagenum = $pagenum.$gap.$rowfill;
26673  } else {
26674  $pagenum = $rowfill.$gap.$pagenum;
26675  }
26676  // write the number
26677  $this->Cell($tw, 0, $pagenum, 0, 1, $alignnum, 0, $link, 0);
26678  }
26679  $page_last = $this->getPage();
26680  $numpages = ($page_last - $page_first + 1);
26681  // account for booklet mode
26682  if ($this->booklet) {
26683  // check if a blank page is required before TOC
26684  $page_fill_start = ((($page_first % 2) == 0) XOR (($page % 2) == 0));
26685  $page_fill_end = (!((($numpages % 2) == 0) XOR ($page_fill_start)));
26686  if ($page_fill_start) {
26687  // add a page at the end (to be moved before TOC)
26688  $this->addPage();
26689  ++$page_last;
26690  ++$numpages;
26691  }
26692  if ($page_fill_end) {
26693  // add a page at the end
26694  $this->addPage();
26695  ++$page_last;
26696  ++$numpages;
26697  }
26698  }
26699  $maxpage = max($maxpage, $page_last);
26700  if (!$this->empty_string($page)) {
26701  for ($p = $page_first; $p <= $page_last; ++$p) {
26702  // get page data
26703  $temppage = $this->getPageBuffer($p);
26704  for ($n = 1; $n <= $maxpage; ++$n) {
26705  // update page numbers
26706  $a = '{#'.$n.'}';
26707  // get page number aliases
26708  $pnalias = $this->getInternalPageNumberAliases($a);
26709  // calculate replacement number
26710  if (($n >= $page) AND ($n <= $this->numpages)) {
26711  $np = $n + $numpages;
26712  } else {
26713  $np = $n;
26714  }
26715  $na = $this->formatTOCPageNumber(($this->starting_page_number + $np - 1));
26716  $nu = $this->UTF8ToUTF16BE($na, false);
26717  // replace aliases with numbers
26718  foreach ($pnalias['u'] as $u) {
26719  $sfill = str_repeat($filler, max(0, (strlen($u) - strlen($nu.' '))));
26720  if ($this->rtl) {
26721  $nr = $nu.$this->UTF8ToUTF16BE(' '.$sfill);
26722  } else {
26723  $nr = $this->UTF8ToUTF16BE($sfill.' ').$nu;
26724  }
26725  $temppage = str_replace($u, $nr, $temppage);
26726  }
26727  foreach ($pnalias['a'] as $a) {
26728  $sfill = str_repeat($filler, max(0, (strlen($a) - strlen($na.' '))));
26729  if ($this->rtl) {
26730  $nr = $na.' '.$sfill;
26731  } else {
26732  $nr = $sfill.' '.$na;
26733  }
26734  $temppage = str_replace($a, $nr, $temppage);
26735  }
26736  }
26737  // save changes
26738  $this->setPageBuffer($p, $temppage);
26739  }
26740  // move pages
26741  $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color);
26742  if ($page_fill_start) {
26743  $this->movePage($page_last, $page_first);
26744  }
26745  for ($i = 0; $i < $numpages; ++$i) {
26746  $this->movePage($page_last, $page);
26747  }
26748  }
26749  }
26750 
26767  public function addHTMLTOC($page='', $toc_name='TOC', $templates=array(), $correct_align=true, $style='', $color=array(0,0,0)) {
26768  $filler = ' ';
26769  $prev_htmlLinkColorArray = $this->htmlLinkColorArray;
26770  $prev_htmlLinkFontStyle = $this->htmlLinkFontStyle;
26771  // set new style for link
26772  $this->htmlLinkColorArray = array();
26773  $this->htmlLinkFontStyle = '';
26774  $page_first = $this->getPage();
26775  $page_fill_start = false;
26776  $page_fill_end = false;
26777  // get the font type used for numbers in each template
26778  $current_font = $this->FontFamily;
26779  foreach ($templates as $level => $html) {
26780  $dom = $this->getHtmlDomArray($html);
26781  foreach ($dom as $key => $value) {
26782  if ($value['value'] == '#TOC_PAGE_NUMBER#') {
26783  $this->SetFont($dom[($key - 1)]['fontname']);
26784  $templates['F'.$level] = $this->isUnicodeFont();
26785  }
26786  }
26787  }
26788  $this->SetFont($current_font);
26789  $maxpage = 0; //used for pages on attached documents
26790  foreach ($this->outlines as $key => $outline) {
26791  // get HTML template
26792  $row = $templates[$outline['l']];
26793  if ($this->empty_string($page)) {
26794  $pagenum = $outline['p'];
26795  } else {
26796  // placemark to be replaced with the correct number
26797  $pagenum = '{#'.($outline['p']).'}';
26798  if ($templates['F'.$outline['l']]) {
26799  $pagenum = '{'.$pagenum.'}';
26800  }
26801  $maxpage = max($maxpage, $outline['p']);
26802  }
26803  // replace templates with current values
26804  $row = str_replace('#TOC_DESCRIPTION#', $outline['t'], $row);
26805  $row = str_replace('#TOC_PAGE_NUMBER#', $pagenum, $row);
26806  // add link to page
26807  $row = '<a href="#'.$outline['p'].','.$outline['y'].'">'.$row.'</a>';
26808  // write bookmark entry
26809  $this->writeHTML($row, false, false, true, false, '');
26810  }
26811  // restore link styles
26812  $this->htmlLinkColorArray = $prev_htmlLinkColorArray;
26813  $this->htmlLinkFontStyle = $prev_htmlLinkFontStyle;
26814  // move TOC page and replace numbers
26815  $page_last = $this->getPage();
26816  $numpages = ($page_last - $page_first + 1);
26817  // account for booklet mode
26818  if ($this->booklet) {
26819  // check if a blank page is required before TOC
26820  $page_fill_start = ((($page_first % 2) == 0) XOR (($page % 2) == 0));
26821  $page_fill_end = (!((($numpages % 2) == 0) XOR ($page_fill_start)));
26822  if ($page_fill_start) {
26823  // add a page at the end (to be moved before TOC)
26824  $this->addPage();
26825  ++$page_last;
26826  ++$numpages;
26827  }
26828  if ($page_fill_end) {
26829  // add a page at the end
26830  $this->addPage();
26831  ++$page_last;
26832  ++$numpages;
26833  }
26834  }
26835  $maxpage = max($maxpage, $page_last);
26836  if (!$this->empty_string($page)) {
26837  for ($p = $page_first; $p <= $page_last; ++$p) {
26838  // get page data
26839  $temppage = $this->getPageBuffer($p);
26840  for ($n = 1; $n <= $maxpage; ++$n) {
26841  // update page numbers
26842  $a = '{#'.$n.'}';
26843  // get page number aliases
26844  $pnalias = $this->getInternalPageNumberAliases($a);
26845  // calculate replacement number
26846  if ($n >= $page) {
26847  $np = $n + $numpages;
26848  } else {
26849  $np = $n;
26850  }
26851  $na = $this->formatTOCPageNumber(($this->starting_page_number + $np - 1));
26852  $nu = $this->UTF8ToUTF16BE($na, false);
26853  // replace aliases with numbers
26854  foreach ($pnalias['u'] as $u) {
26855  if ($correct_align) {
26856  $sfill = str_repeat($filler, (strlen($u) - strlen($nu.' ')));
26857  if ($this->rtl) {
26858  $nr = $nu.$this->UTF8ToUTF16BE(' '.$sfill);
26859  } else {
26860  $nr = $this->UTF8ToUTF16BE($sfill.' ').$nu;
26861  }
26862  } else {
26863  $nr = $nu;
26864  }
26865  $temppage = str_replace($u, $nr, $temppage);
26866  }
26867  foreach ($pnalias['a'] as $a) {
26868  if ($correct_align) {
26869  $sfill = str_repeat($filler, (strlen($a) - strlen($na.' ')));
26870  if ($this->rtl) {
26871  $nr = $na.' '.$sfill;
26872  } else {
26873  $nr = $sfill.' '.$na;
26874  }
26875  } else {
26876  $nr = $na;
26877  }
26878  $temppage = str_replace($a, $nr, $temppage);
26879  }
26880  }
26881  // save changes
26882  $this->setPageBuffer($p, $temppage);
26883  }
26884  // move pages
26885  $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color);
26886  if ($page_fill_start) {
26887  $this->movePage($page_last, $page_first);
26888  }
26889  for ($i = 0; $i < $numpages; ++$i) {
26890  $this->movePage($page_last, $page);
26891  }
26892  }
26893  }
26894 
26900  public function startTransaction() {
26901  if (isset($this->objcopy)) {
26902  // remove previous copy
26903  $this->commitTransaction();
26904  }
26905  // record current page number and Y position
26906  $this->start_transaction_page = $this->page;
26907  $this->start_transaction_y = $this->y;
26908  // clone current object
26909  $this->objcopy = $this->objclone($this);
26910  }
26911 
26917  public function commitTransaction() {
26918  if (isset($this->objcopy)) {
26919  $this->objcopy->_destroy(true, true);
26920  unset($this->objcopy);
26921  }
26922  }
26923 
26931  public function rollbackTransaction($self=false) {
26932  if (isset($this->objcopy)) {
26933  if (isset($this->objcopy->diskcache) AND $this->objcopy->diskcache) {
26934  // truncate files to previous values
26935  foreach ($this->objcopy->cache_file_length as $file => $length) {
26936  $file = substr($file, 1);
26937  $handle = fopen($file, 'r+');
26938  ftruncate($handle, $length);
26939  }
26940  }
26941  $this->_destroy(true, true);
26942  if ($self) {
26943  $objvars = get_object_vars($this->objcopy);
26944  foreach ($objvars as $key => $value) {
26945  $this->$key = $value;
26946  }
26947  }
26948  return $this->objcopy;
26949  }
26950  return $this;
26951  }
26952 
26960  public function objclone($object) {
26961  return @clone($object);
26962  }
26963 
26971  public function empty_string($str) {
26972  return (is_null($str) OR (is_string($str) AND (strlen($str) == 0)));
26973  }
26974 
26984  public function revstrpos($haystack, $needle, $offset = 0) {
26985  $length = strlen($haystack);
26986  $offset = ($offset > 0)?($length - $offset):abs($offset);
26987  $pos = strpos(strrev($haystack), strrev($needle), $offset);
26988  return ($pos === false)?false:($length - $pos - strlen($needle));
26989  }
26990 
26991  // --- MULTI COLUMNS METHODS -----------------------
26992 
27001  public function setEqualColumns($numcols=0, $width=0, $y='') {
27002  $this->columns = array();
27003  if ($numcols < 2) {
27004  $numcols = 0;
27005  $this->columns = array();
27006  } else {
27007  // maximum column width
27008  $maxwidth = ($this->w - $this->original_lMargin - $this->original_rMargin) / $numcols;
27009  if (($width == 0) OR ($width > $maxwidth)) {
27010  $width = $maxwidth;
27011  }
27012  if ($this->empty_string($y)) {
27013  $y = $this->y;
27014  }
27015  // space between columns
27016  $space = (($this->w - $this->original_lMargin - $this->original_rMargin - ($numcols * $width)) / ($numcols - 1));
27017  // fill the columns array (with, space, starting Y position)
27018  for ($i = 0; $i < $numcols; ++$i) {
27019  $this->columns[$i] = array('w' => $width, 's' => $space, 'y' => $y);
27020  }
27021  }
27022  $this->num_columns = $numcols;
27023  $this->current_column = 0;
27024  $this->column_start_page = $this->page;
27025  $this->selectColumn(0);
27026  }
27027 
27033  public function resetColumns() {
27034  $this->lMargin = $this->original_lMargin;
27035  $this->rMargin = $this->original_rMargin;
27036  $this->setEqualColumns();
27037  }
27038 
27046  public function setColumnsArray($columns) {
27047  $this->columns = $columns;
27048  $this->num_columns = count($columns);
27049  $this->current_column = 0;
27050  $this->column_start_page = $this->page;
27051  $this->selectColumn(0);
27052  }
27053 
27060  public function selectColumn($col='') {
27061  if (is_string($col)) {
27062  $col = $this->current_column;
27063  } elseif ($col >= $this->num_columns) {
27064  $col = 0;
27065  }
27066  $xshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
27067  $enable_thead = false;
27068  if ($this->num_columns > 1) {
27069  if ($col != $this->current_column) {
27070  // move Y pointer at the top of the column
27071  if ($this->column_start_page == $this->page) {
27072  $this->y = $this->columns[$col]['y'];
27073  } else {
27074  $this->y = $this->tMargin;
27075  }
27076  // Avoid to write table headers more than once
27077  if (($this->page > $this->maxselcol['page']) OR (($this->page == $this->maxselcol['page']) AND ($col > $this->maxselcol['column']))) {
27078  $enable_thead = true;
27079  $this->maxselcol['page'] = $this->page;
27080  $this->maxselcol['column'] = $col;
27081  }
27082  }
27083  $xshift = $this->colxshift;
27084  // set X position of the current column by case
27085  $listindent = ($this->listindentlevel * $this->listindent);
27086  // calculate column X position
27087  $colpos = 0;
27088  for ($i = 0; $i < $col; ++$i) {
27089  $colpos += ($this->columns[$i]['w'] + $this->columns[$i]['s']);
27090  }
27091  if ($this->rtl) {
27092  $x = $this->w - $this->original_rMargin - $colpos;
27093  $this->rMargin = ($this->w - $x + $listindent);
27094  $this->lMargin = ($x - $this->columns[$col]['w']);
27095  $this->x = $x - $listindent;
27096  } else {
27097  $x = $this->original_lMargin + $colpos;
27098  $this->lMargin = ($x + $listindent);
27099  $this->rMargin = ($this->w - $x - $this->columns[$col]['w']);
27100  $this->x = $x + $listindent;
27101  }
27102  $this->columns[$col]['x'] = $x;
27103  }
27104  $this->current_column = $col;
27105  // fix for HTML mode
27106  $this->newline = true;
27107  // print HTML table header (if any)
27108  if ((!$this->empty_string($this->thead)) AND (!$this->inthead)) {
27109  if ($enable_thead) {
27110  // print table header
27111  $this->writeHTML($this->thead, false, false, false, false, '');
27112  $this->y += $xshift['s']['V'];
27113  // store end of header position
27114  if (!isset($this->columns[$col]['th'])) {
27115  $this->columns[$col]['th'] = array();
27116  }
27117  $this->columns[$col]['th']['\''.$this->page.'\''] = $this->y;
27118  $this->lasth = 0;
27119  } elseif (isset($this->columns[$col]['th']['\''.$this->page.'\''])) {
27120  $this->y = $this->columns[$col]['th']['\''.$this->page.'\''];
27121  }
27122  }
27123  // account for an html table cell over multiple columns
27124  if ($this->rtl) {
27125  $this->rMargin += $xshift['x'];
27126  $this->x -= ($xshift['x'] + $xshift['p']['R']);
27127  } else {
27128  $this->lMargin += $xshift['x'];
27129  $this->x += $xshift['x'] + $xshift['p']['L'];
27130  }
27131  }
27132 
27139  public function getColumn() {
27140  return $this->current_column;
27141  }
27142 
27149  public function getNumberOfColumns() {
27150  return $this->num_columns;
27151  }
27152 
27160  public function serializeTCPDFtagParameters($pararray) {
27161  return urlencode(serialize($pararray));
27162  }
27163 
27172  public function setTextRenderingMode($stroke=0, $fill=true, $clip=false) {
27173  // Ref.: PDF 32000-1:2008 - 9.3.6 Text Rendering Mode
27174  // convert text rendering parameters
27175  if ($stroke < 0) {
27176  $stroke = 0;
27177  }
27178  if ($fill === true) {
27179  if ($stroke > 0) {
27180  if ($clip === true) {
27181  // Fill, then stroke text and add to path for clipping
27182  $textrendermode = 6;
27183  } else {
27184  // Fill, then stroke text
27185  $textrendermode = 2;
27186  }
27187  $textstrokewidth = $stroke;
27188  } else {
27189  if ($clip === true) {
27190  // Fill text and add to path for clipping
27191  $textrendermode = 4;
27192  } else {
27193  // Fill text
27194  $textrendermode = 0;
27195  }
27196  }
27197  } else {
27198  if ($stroke > 0) {
27199  if ($clip === true) {
27200  // Stroke text and add to path for clipping
27201  $textrendermode = 5;
27202  } else {
27203  // Stroke text
27204  $textrendermode = 1;
27205  }
27206  $textstrokewidth = $stroke;
27207  } else {
27208  if ($clip === true) {
27209  // Add text to path for clipping
27210  $textrendermode = 7;
27211  } else {
27212  // Neither fill nor stroke text (invisible)
27213  $textrendermode = 3;
27214  }
27215  }
27216  }
27217  $this->textrendermode = $textrendermode;
27218  $this->textstrokewidth = $stroke;
27219  }
27220 
27227  public function setTextShadow($params=array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=>'Normal')) {
27228  if (isset($params['enabled'])) {
27229  $this->txtshadow['enabled'] = $params['enabled']?true:false;
27230  } else {
27231  $this->txtshadow['enabled'] = false;
27232  }
27233  if (isset($params['depth_w'])) {
27234  $this->txtshadow['depth_w'] = floatval($params['depth_w']);
27235  } else {
27236  $this->txtshadow['depth_w'] = 0;
27237  }
27238  if (isset($params['depth_h'])) {
27239  $this->txtshadow['depth_h'] = floatval($params['depth_h']);
27240  } else {
27241  $this->txtshadow['depth_h'] = 0;
27242  }
27243  if (isset($params['color']) AND ($params['color'] !== false) AND is_array($params['color'])) {
27244  $this->txtshadow['color'] = $params['color'];
27245  } else {
27246  $this->txtshadow['color'] = $this->strokecolor;
27247  }
27248  if (isset($params['opacity'])) {
27249  $this->txtshadow['opacity'] = min(1, max(0, floatval($params['opacity'])));
27250  } else {
27251  $this->txtshadow['opacity'] = 1;
27252  }
27253  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'))) {
27254  $this->txtshadow['blend_mode'] = $params['blend_mode'];
27255  } else {
27256  $this->txtshadow['blend_mode'] = 'Normal';
27257  }
27258  if ((($this->txtshadow['depth_w'] == 0) AND ($this->txtshadow['depth_h'] == 0)) OR ($this->txtshadow['opacity'] == 0)) {
27259  $this->txtshadow['enabled'] = false;
27260  }
27261  }
27262 
27269  public function getTextShadow() {
27270  return $this->txtshadow;
27271  }
27272 
27287  protected function hyphenateWord($word, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
27288  $hyphenword = array(); // hyphens positions
27289  $numchars = count($word);
27290  if ($numchars <= $charmin) {
27291  return $word;
27292  }
27293  $word_string = $this->UTF8ArrSubString($word);
27294  // some words will be returned as-is
27295  $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})(\]?)$/';
27296  if (preg_match($pattern, $word_string) > 0) {
27297  // email
27298  return $word;
27299  }
27300  $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})(\]?)$/';
27301  if (preg_match($pattern, $word_string) > 0) {
27302  // URL
27303  return $word;
27304  }
27305  if (isset($dictionary[$word_string])) {
27306  return $this->UTF8StringToArray($dictionary[$word_string]);
27307  }
27308  // suround word with '_' characters
27309  $tmpword = array_merge(array(95), $word, array(95));
27310  $tmpnumchars = $numchars + 2;
27311  $maxpos = $tmpnumchars - $charmin;
27312  for ($pos = 0; $pos < $maxpos; ++$pos) {
27313  $imax = min(($tmpnumchars - $pos), $charmax);
27314  for ($i = $charmin; $i <= $imax; ++$i) {
27315  $subword = strtolower($this->UTF8ArrSubString($tmpword, $pos, $pos + $i));
27316  if (isset($patterns[$subword])) {
27317  $pattern = $this->UTF8StringToArray($patterns[$subword]);
27318  $pattern_length = count($pattern);
27319  $digits = 1;
27320  for ($j = 0; $j < $pattern_length; ++$j) {
27321  // check if $pattern[$j] is a number
27322  if (($pattern[$j] >= 48) AND ($pattern[$j] <= 57)) {
27323  if ($j == 0) {
27324  $zero = $pos - 1;
27325  } else {
27326  $zero = $pos + $j - $digits;
27327  }
27328  if (!isset($hyphenword[$zero]) OR ($hyphenword[$zero] != $pattern[$j])) {
27329  $hyphenword[$zero] = $this->unichr($pattern[$j]);
27330  }
27331  ++$digits;
27332  }
27333  }
27334  }
27335  }
27336  }
27337  $inserted = 0;
27338  $maxpos = $numchars - $rightmin;
27339  for ($i = $leftmin; $i <= $maxpos; ++$i) {
27340  if (isset($hyphenword[$i]) AND (($hyphenword[$i] % 2) != 0)) {
27341  // 173 = soft hyphen character
27342  array_splice($word, $i + $inserted, 0, 173);
27343  ++$inserted;
27344  }
27345  }
27346  return $word;
27347  }
27348 
27357  public function getHyphenPatternsFromTEX($file) {
27358  // TEX patterns are available at:
27359  // http://www.ctan.org/tex-archive/language/hyph-utf8/tex/generic/hyph-utf8/patterns/
27360  $data = file_get_contents($file);
27361  $patterns = array();
27362  // remove comments
27363  $data = preg_replace('/\%[^\n]*/', '', $data);
27364  // extract the patterns part
27365  preg_match('/\\\\patterns\{([^\}]*)\}/i', $data, $matches);
27366  $data = trim(substr($matches[0], 10, -1));
27367  // extract each pattern
27368  $patterns_array = preg_split('/[\s]+/', $data);
27369  // create new language array of patterns
27370  $patterns = array();
27371  foreach($patterns_array as $val) {
27372  if (!$this->empty_string($val)) {
27373  $val = trim($val);
27374  $val = str_replace('\'', '\\\'', $val);
27375  $key = preg_replace('/[0-9]+/', '', $val);
27376  $patterns[$key] = $val;
27377  }
27378  }
27379  return $patterns;
27380  }
27381 
27396  public function hyphenateText($text, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
27397  $text = $this->unhtmlentities($text);
27398  $word = array(); // last word
27399  $txtarr = array(); // text to be returned
27400  $intag = false; // true if we are inside an HTML tag
27401  if (!is_array($patterns)) {
27402  $patterns = $this->getHyphenPatternsFromTEX($patterns);
27403  }
27404  // get array of characters
27405  $unichars = $this->UTF8StringToArray($text);
27406  // for each char
27407  foreach ($unichars as $char) {
27408  if ((!$intag) AND $this->unicode->uni_type[$char] == 'L') {
27409  // letter character
27410  $word[] = $char;
27411  } else {
27412  // other type of character
27413  if (!$this->empty_string($word)) {
27414  // hypenate the word
27415  $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
27416  $word = array();
27417  }
27418  $txtarr[] = $char;
27419  if (chr($char) == '<') {
27420  // we are inside an HTML tag
27421  $intag = true;
27422  } elseif ($intag AND (chr($char) == '>')) {
27423  // end of HTML tag
27424  $intag = false;
27425  }
27426  }
27427  }
27428  if (!$this->empty_string($word)) {
27429  // hypenate the word
27430  $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
27431  }
27432  // convert char array to string and return
27433  return $this->UTF8ArrSubString($txtarr);
27434  }
27435 
27442  public function setRasterizeVectorImages($mode) {
27443  $this->rasterize_vector_images = $mode;
27444  }
27445 
27467  protected function getPathPaintOperator($style, $default='S') {
27468  $op = '';
27469  switch($style) {
27470  case 'S':
27471  case 'D': {
27472  $op = 'S';
27473  break;
27474  }
27475  case 's':
27476  case 'd': {
27477  $op = 's';
27478  break;
27479  }
27480  case 'f':
27481  case 'F': {
27482  $op = 'f';
27483  break;
27484  }
27485  case 'f*':
27486  case 'F*': {
27487  $op = 'f*';
27488  break;
27489  }
27490  case 'B':
27491  case 'FD':
27492  case 'DF': {
27493  $op = 'B';
27494  break;
27495  }
27496  case 'B*':
27497  case 'F*D':
27498  case 'DF*': {
27499  $op = 'B*';
27500  break;
27501  }
27502  case 'b':
27503  case 'fd':
27504  case 'df': {
27505  $op = 'b';
27506  break;
27507  }
27508  case 'b*':
27509  case 'f*d':
27510  case 'df*': {
27511  $op = 'b*';
27512  break;
27513  }
27514  case 'CNZ': {
27515  $op = 'W n';
27516  break;
27517  }
27518  case 'CEO': {
27519  $op = 'W* n';
27520  break;
27521  }
27522  case 'n': {
27523  $op = 'n';
27524  break;
27525  }
27526  default: {
27527  if (!empty($default)) {
27528  $op = $this->getPathPaintOperator($default, '');
27529  } else {
27530  $op = '';
27531  }
27532  }
27533  }
27534  return $op;
27535  }
27536 
27544  public function setFontSubsetting($enable=true) {
27545  if ($this->pdfa_mode) {
27546  $this->font_subsetting = false;
27547  } else {
27548  $this->font_subsetting = $enable ? true : false;
27549  }
27550  }
27551 
27559  public function getFontSubsetting() {
27560  return $this->font_subsetting;
27561  }
27562 
27572  public function stringLeftTrim($str, $replace='') {
27573  return preg_replace('/^'.$this->re_space['p'].'+/'.$this->re_space['m'], $replace, $str);
27574  }
27575 
27585  public function stringRightTrim($str, $replace='') {
27586  return preg_replace('/'.$this->re_space['p'].'+$/'.$this->re_space['m'], $replace, $str);
27587  }
27588 
27598  public function stringTrim($str, $replace='') {
27599  $str = $this->stringLeftTrim($str, $replace);
27600  $str = $this->stringRightTrim($str, $replace);
27601  return $str;
27602  }
27603 
27611  public function isUnicodeFont() {
27612  return (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0'));
27613  }
27614 
27623  public function getFontFamilyName($fontfamily) {
27624  // remove spaces and symbols
27625  $fontfamily = preg_replace('/[^a-z0-9_\,]/', '', strtolower($fontfamily));
27626  // extract all font names
27627  $fontslist = preg_split('/[,]/', $fontfamily);
27628  // find first valid font name
27629  foreach ($fontslist as $font) {
27630  // replace font variations
27631  $font = preg_replace('/italic$/', 'I', $font);
27632  $font = preg_replace('/oblique$/', 'I', $font);
27633  $font = preg_replace('/bold([I]?)$/', 'B\\1', $font);
27634  // replace common family names and core fonts
27635  $pattern = array();
27636  $replacement = array();
27637  $pattern[] = '/^serif|^cursive|^fantasy|^timesnewroman/';
27638  $replacement[] = 'times';
27639  $pattern[] = '/^sansserif/';
27640  $replacement[] = 'helvetica';
27641  $pattern[] = '/^monospace/';
27642  $replacement[] = 'courier';
27643  $font = preg_replace($pattern, $replacement, $font);
27644  if (in_array(strtolower($font), $this->fontlist) OR in_array($font, $this->fontkeys)) {
27645  return $font;
27646  }
27647  }
27648  // return current font as default
27649  return $this->CurrentFont['fontkey'];
27650  }
27651 
27666  public function startTemplate($w=0, $h=0, $group=false) {
27667  if ($this->inxobj) {
27668  // we are already inside an XObject template
27669  return false;
27670  }
27671  $this->inxobj = true;
27672  ++$this->n;
27673  // XObject ID
27674  $this->xobjid = 'XT'.$this->n;
27675  // object ID
27676  $this->xobjects[$this->xobjid] = array('n' => $this->n);
27677  // store current graphic state
27678  $this->xobjects[$this->xobjid]['gvars'] = $this->getGraphicVars();
27679  // initialize data
27680  $this->xobjects[$this->xobjid]['intmrk'] = 0;
27681  $this->xobjects[$this->xobjid]['transfmrk'] = array();
27682  $this->xobjects[$this->xobjid]['outdata'] = '';
27683  $this->xobjects[$this->xobjid]['xobjects'] = array();
27684  $this->xobjects[$this->xobjid]['images'] = array();
27685  $this->xobjects[$this->xobjid]['fonts'] = array();
27686  $this->xobjects[$this->xobjid]['annotations'] = array();
27687  $this->xobjects[$this->xobjid]['extgstates'] = array();
27688  $this->xobjects[$this->xobjid]['gradients'] = array();
27689  $this->xobjects[$this->xobjid]['spot_colors'] = array();
27690  // set new environment
27691  $this->num_columns = 1;
27692  $this->current_column = 0;
27693  $this->SetAutoPageBreak(false);
27694  if (($w === '') OR ($w <= 0)) {
27695  $w = $this->w - $this->lMargin - $this->rMargin;
27696  }
27697  if (($h === '') OR ($h <= 0)) {
27698  $h = $this->h - $this->tMargin - $this->bMargin;
27699  }
27700  $this->xobjects[$this->xobjid]['x'] = 0;
27701  $this->xobjects[$this->xobjid]['y'] = 0;
27702  $this->xobjects[$this->xobjid]['w'] = $w;
27703  $this->xobjects[$this->xobjid]['h'] = $h;
27704  $this->w = $w;
27705  $this->h = $h;
27706  $this->wPt = $this->w * $this->k;
27707  $this->hPt = $this->h * $this->k;
27708  $this->fwPt = $this->wPt;
27709  $this->fhPt = $this->hPt;
27710  $this->x = 0;
27711  $this->y = 0;
27712  $this->lMargin = 0;
27713  $this->rMargin = 0;
27714  $this->tMargin = 0;
27715  $this->bMargin = 0;
27716  // set group mode
27717  $this->xobjects[$this->xobjid]['group'] = $group;
27718  return $this->xobjid;
27719  }
27720 
27731  public function endTemplate() {
27732  if (!$this->inxobj) {
27733  // we are not inside a template
27734  return false;
27735  }
27736  $this->inxobj = false;
27737  // restore previous graphic state
27738  $this->setGraphicVars($this->xobjects[$this->xobjid]['gvars'], true);
27739  return $this->xobjid;
27740  }
27741 
27760  public function printTemplate($id, $x='', $y='', $w=0, $h=0, $align='', $palign='', $fitonpage=false) {
27761  if ($this->state != 2) {
27762  return;
27763  }
27764  if (!isset($this->xobjects[$id])) {
27765  $this->Error('The XObject Template \''.$id.'\' doesn\'t exist!');
27766  }
27767  if ($this->inxobj) {
27768  if ($id == $this->xobjid) {
27769  // close current template
27770  $this->endTemplate();
27771  } else {
27772  // use the template as resource for the template currently opened
27773  $this->xobjects[$this->xobjid]['xobjects'][$id] = $this->xobjects[$id];
27774  }
27775  }
27776  // set default values
27777  if ($x === '') {
27778  $x = $this->x;
27779  }
27780  if ($y === '') {
27781  $y = $this->y;
27782  }
27783  // check page for no-write regions and adapt page margins if necessary
27784  list($x, $y) = $this->checkPageRegions($h, $x, $y);
27785  $ow = $this->xobjects[$id]['w'];
27786  $oh = $this->xobjects[$id]['h'];
27787  // calculate template width and height on document
27788  if (($w <= 0) AND ($h <= 0)) {
27789  $w = $ow;
27790  $h = $oh;
27791  } elseif ($w <= 0) {
27792  $w = $h * $ow / $oh;
27793  } elseif ($h <= 0) {
27794  $h = $w * $oh / $ow;
27795  }
27796  // fit the template on available space
27797  list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
27798  // set page alignment
27799  $rb_y = $y + $h;
27800  // set alignment
27801  if ($this->rtl) {
27802  if ($palign == 'L') {
27803  $xt = $this->lMargin;
27804  } elseif ($palign == 'C') {
27805  $xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
27806  } elseif ($palign == 'R') {
27807  $xt = $this->w - $this->rMargin - $w;
27808  } else {
27809  $xt = $x - $w;
27810  }
27811  $rb_x = $xt;
27812  } else {
27813  if ($palign == 'L') {
27814  $xt = $this->lMargin;
27815  } elseif ($palign == 'C') {
27816  $xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
27817  } elseif ($palign == 'R') {
27818  $xt = $this->w - $this->rMargin - $w;
27819  } else {
27820  $xt = $x;
27821  }
27822  $rb_x = $xt + $w;
27823  }
27824  // print XObject Template + Transformation matrix
27825  $this->StartTransform();
27826  // translate and scale
27827  $sx = ($w / $this->xobjects[$id]['w']);
27828  $sy = ($h / $this->xobjects[$id]['h']);
27829  $tm = array();
27830  $tm[0] = $sx;
27831  $tm[1] = 0;
27832  $tm[2] = 0;
27833  $tm[3] = $sy;
27834  $tm[4] = $xt * $this->k;
27835  $tm[5] = ($this->h - $h - $y) * $this->k;
27836  $this->Transform($tm);
27837  // set object
27838  $this->_out('/'.$id.' Do');
27839  $this->StopTransform();
27840  // add annotations
27841  if (!empty($this->xobjects[$id]['annotations'])) {
27842  foreach ($this->xobjects[$id]['annotations'] as $annot) {
27843  // transform original coordinates
27844  $coordlt = $this->getTransformationMatrixProduct($tm, array(1, 0, 0, 1, ($annot['x'] * $this->k), (-$annot['y'] * $this->k)));
27845  $ax = ($coordlt[4] / $this->k);
27846  $ay = ($this->h - $h - ($coordlt[5] / $this->k));
27847  $coordrb = $this->getTransformationMatrixProduct($tm, array(1, 0, 0, 1, (($annot['x'] + $annot['w']) * $this->k), ((-$annot['y'] - $annot['h']) * $this->k)));
27848  $aw = ($coordrb[4] / $this->k) - $ax;
27849  $ah = ($this->h - $h - ($coordrb[5] / $this->k)) - $ay;
27850  $this->Annotation($ax, $ay, $aw, $ah, $annot['text'], $annot['opt'], $annot['spaces']);
27851  }
27852  }
27853  // set pointer to align the next text/objects
27854  switch($align) {
27855  case 'T': {
27856  $this->y = $y;
27857  $this->x = $rb_x;
27858  break;
27859  }
27860  case 'M': {
27861  $this->y = $y + round($h/2);
27862  $this->x = $rb_x;
27863  break;
27864  }
27865  case 'B': {
27866  $this->y = $rb_y;
27867  $this->x = $rb_x;
27868  break;
27869  }
27870  case 'N': {
27871  $this->SetY($rb_y);
27872  break;
27873  }
27874  default:{
27875  break;
27876  }
27877  }
27878  }
27879 
27887  public function setFontStretching($perc=100) {
27888  $this->font_stretching = $perc;
27889  }
27890 
27898  public function getFontStretching() {
27899  return $this->font_stretching;
27900  }
27901 
27909  public function setFontSpacing($spacing=0) {
27910  $this->font_spacing = $spacing;
27911  }
27912 
27920  public function getFontSpacing() {
27921  return $this->font_spacing;
27922  }
27923 
27932  public function getPageRegions() {
27933  return $this->page_regions;
27934  }
27935 
27947  public function setPageRegions($regions=array()) {
27948  // empty current regions array
27949  $this->page_regions = array();
27950  // add regions
27951  foreach ($regions as $data) {
27952  $this->addPageRegion($data);
27953  }
27954  }
27955 
27967  public function addPageRegion($region) {
27968  if (!isset($region['page']) OR empty($region['page'])) {
27969  $region['page'] = $this->page;
27970  }
27971  if (isset($region['xt']) AND isset($region['xb']) AND ($region['xt'] > 0) AND ($region['xb'] > 0)
27972  AND isset($region['yt']) AND isset($region['yb']) AND ($region['yt'] >= 0) AND ($region['yt'] < $region['yb'])
27973  AND isset($region['side']) AND (($region['side'] == 'L') OR ($region['side'] == 'R'))) {
27974  $this->page_regions[] = $region;
27975  }
27976  }
27977 
27986  public function removePageRegion($key) {
27987  if (isset($this->page_regions[$key])) {
27988  unset($this->page_regions[$key]);
27989  }
27990  }
27991 
28004  protected function checkPageRegions($h, $x, $y) {
28005  // set default values
28006  if ($x === '') {
28007  $x = $this->x;
28008  }
28009  if ($y === '') {
28010  $y = $this->y;
28011  }
28012  if (!$this->check_page_regions OR empty($this->page_regions)) {
28013  // no page regions defined
28014  return array($x, $y);
28015  }
28016  if (empty($h)) {
28017  $h = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
28018  }
28019  // check for page break
28020  if ($this->checkPageBreak($h, $y)) {
28021  // the content will be printed on a new page
28022  $x = $this->x;
28023  $y = $this->y;
28024  }
28025  if ($this->num_columns > 1) {
28026  if ($this->rtl) {
28027  $this->lMargin = ($this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w']);
28028  } else {
28029  $this->rMargin = ($this->w - $this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w']);
28030  }
28031  } else {
28032  if ($this->rtl) {
28033  $this->lMargin = max($this->clMargin, $this->original_lMargin);
28034  } else {
28035  $this->rMargin = max($this->crMargin, $this->original_rMargin);
28036  }
28037  }
28038  // adjust coordinates and page margins
28039  foreach ($this->page_regions as $regid => $regdata) {
28040  if ($regdata['page'] == $this->page) {
28041  // check region boundaries
28042  if (($y > ($regdata['yt'] - $h)) AND ($y <= $regdata['yb'])) {
28043  // Y is inside the region
28044  $minv = ($regdata['xb'] - $regdata['xt']) / ($regdata['yb'] - $regdata['yt']); // inverse of angular coefficient
28045  $yt = max($y, $regdata['yt']);
28046  $yb = min(($yt + $h), $regdata['yb']);
28047  $xt = (($yt - $regdata['yt']) * $minv) + $regdata['xt'];
28048  $xb = (($yb - $regdata['yt']) * $minv) + $regdata['xt'];
28049  if ($regdata['side'] == 'L') { // left side
28050  $new_margin = max($xt, $xb);
28051  if ($this->lMargin < $new_margin) {
28052  if ($this->rtl) {
28053  // adjust left page margin
28054  $this->lMargin = max(0, $new_margin);
28055  }
28056  if ($x < $new_margin) {
28057  // adjust x position
28058  $x = $new_margin;
28059  if ($new_margin > ($this->w - $this->rMargin)) {
28060  // adjust y position
28061  $y = $regdata['yb'] - $h;
28062  }
28063  }
28064  }
28065  } elseif ($regdata['side'] == 'R') { // right side
28066  $new_margin = min($xt, $xb);
28067  if (($this->w - $this->rMargin) > $new_margin) {
28068  if (!$this->rtl) {
28069  // adjust right page margin
28070  $this->rMargin = max(0, ($this->w - $new_margin));
28071  }
28072  if ($x > $new_margin) {
28073  // adjust x position
28074  $x = $new_margin;
28075  if ($new_margin > $this->lMargin) {
28076  // adjust y position
28077  $y = $regdata['yb'] - $h;
28078  }
28079  }
28080  }
28081  }
28082  }
28083  }
28084  }
28085  return array($x, $y);
28086  }
28087 
28088  // --- SVG METHODS ---------------------------------------------------------
28089 
28107  public function ImageSVG($file, $x='', $y='', $w=0, $h=0, $link='', $align='', $palign='', $border=0, $fitonpage=false) {
28108  if ($this->state != 2) {
28109  return;
28110  }
28111  if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) {
28112  // convert SVG to raster image using GD or ImageMagick libraries
28113  return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
28114  }
28115  if ($file{0} === '@') { // image from string
28116  $this->svgdir = '';
28117  $svgdata = substr($file, 1);
28118  } else { // SVG file
28119  $this->svgdir = dirname($file);
28120  $svgdata = file_get_contents($file);
28121  }
28122  if ($svgdata === false) {
28123  $this->Error('SVG file not found: '.$file);
28124  }
28125  if ($x === '') {
28126  $x = $this->x;
28127  }
28128  if ($y === '') {
28129  $y = $this->y;
28130  }
28131  // check page for no-write regions and adapt page margins if necessary
28132  list($x, $y) = $this->checkPageRegions($h, $x, $y);
28133  $k = $this->k;
28134  $ox = 0;
28135  $oy = 0;
28136  $ow = $w;
28137  $oh = $h;
28138  $aspect_ratio_align = 'xMidYMid';
28139  $aspect_ratio_ms = 'meet';
28140  $regs = array();
28141  // get original image width and height
28142  preg_match('/<svg([^>]*)>/si', $svgdata, $regs);
28143  if (isset($regs[1]) AND !empty($regs[1])) {
28144  $tmp = array();
28145  if (preg_match('/[\s]+x[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
28146  $ox = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false);
28147  }
28148  $tmp = array();
28149  if (preg_match('/[\s]+y[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
28150  $oy = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false);
28151  }
28152  $tmp = array();
28153  if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
28154  $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
28155  }
28156  $tmp = array();
28157  if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
28158  $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
28159  }
28160  $tmp = array();
28161  $view_box = array();
28162  if (preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.\-]+)[\s]+([0-9\.\-]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $regs[1], $tmp)) {
28163  if (count($tmp) == 5) {
28164  array_shift($tmp);
28165  foreach ($tmp as $key => $val) {
28166  $view_box[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false);
28167  }
28168  $ox = $view_box[0];
28169  $oy = $view_box[1];
28170  }
28171  // get aspect ratio
28172  $tmp = array();
28173  if (preg_match('/[\s]+preserveAspectRatio[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
28174  $aspect_ratio = preg_split('/[\s]+/si', $tmp[1]);
28175  switch (count($aspect_ratio)) {
28176  case 3: {
28177  $aspect_ratio_align = $aspect_ratio[1];
28178  $aspect_ratio_ms = $aspect_ratio[2];
28179  break;
28180  }
28181  case 2: {
28182  $aspect_ratio_align = $aspect_ratio[0];
28183  $aspect_ratio_ms = $aspect_ratio[1];
28184  break;
28185  }
28186  case 1: {
28187  $aspect_ratio_align = $aspect_ratio[0];
28188  $aspect_ratio_ms = 'meet';
28189  break;
28190  }
28191  }
28192  }
28193  }
28194  }
28195  // calculate image width and height on document
28196  if (($w <= 0) AND ($h <= 0)) {
28197  // convert image size to document unit
28198  $w = $ow;
28199  $h = $oh;
28200  } elseif ($w <= 0) {
28201  $w = $h * $ow / $oh;
28202  } elseif ($h <= 0) {
28203  $h = $w * $oh / $ow;
28204  }
28205  // fit the image on available space
28206  list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
28207  if ($this->rasterize_vector_images) {
28208  // convert SVG to raster image using GD or ImageMagick libraries
28209  return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
28210  }
28211  // set alignment
28212  $this->img_rb_y = $y + $h;
28213  // set alignment
28214  if ($this->rtl) {
28215  if ($palign == 'L') {
28216  $ximg = $this->lMargin;
28217  } elseif ($palign == 'C') {
28218  $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
28219  } elseif ($palign == 'R') {
28220  $ximg = $this->w - $this->rMargin - $w;
28221  } else {
28222  $ximg = $x - $w;
28223  }
28224  $this->img_rb_x = $ximg;
28225  } else {
28226  if ($palign == 'L') {
28227  $ximg = $this->lMargin;
28228  } elseif ($palign == 'C') {
28229  $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
28230  } elseif ($palign == 'R') {
28231  $ximg = $this->w - $this->rMargin - $w;
28232  } else {
28233  $ximg = $x;
28234  }
28235  $this->img_rb_x = $ximg + $w;
28236  }
28237  // store current graphic vars
28238  $gvars = $this->getGraphicVars();
28239  // store SVG position and scale factors
28240  $svgoffset_x = ($ximg - $ox) * $this->k;
28241  $svgoffset_y = -($y - $oy) * $this->k;
28242  if (isset($view_box[2]) AND ($view_box[2] > 0) AND ($view_box[3] > 0)) {
28243  $ow = $view_box[2];
28244  $oh = $view_box[3];
28245  } else {
28246  if ($ow <= 0) {
28247  $ow = $w;
28248  }
28249  if ($oh <= 0) {
28250  $oh = $h;
28251  }
28252  }
28253  $svgscale_x = $w / $ow;
28254  $svgscale_y = $h / $oh;
28255  // scaling and alignment
28256  if ($aspect_ratio_align != 'none') {
28257  // store current scaling values
28258  $svgscale_old_x = $svgscale_x;
28259  $svgscale_old_y = $svgscale_y;
28260  // force uniform scaling
28261  if ($aspect_ratio_ms == 'slice') {
28262  // the entire viewport is covered by the viewBox
28263  if ($svgscale_x > $svgscale_y) {
28264  $svgscale_y = $svgscale_x;
28265  } elseif ($svgscale_x < $svgscale_y) {
28266  $svgscale_x = $svgscale_y;
28267  }
28268  } else { // meet
28269  // the entire viewBox is visible within the viewport
28270  if ($svgscale_x < $svgscale_y) {
28271  $svgscale_y = $svgscale_x;
28272  } elseif ($svgscale_x > $svgscale_y) {
28273  $svgscale_x = $svgscale_y;
28274  }
28275  }
28276  // correct X alignment
28277  switch (substr($aspect_ratio_align, 1, 3)) {
28278  case 'Min': {
28279  // do nothing
28280  break;
28281  }
28282  case 'Max': {
28283  $svgoffset_x += (($w * $this->k) - ($ow * $this->k * $svgscale_x));
28284  break;
28285  }
28286  default:
28287  case 'Mid': {
28288  $svgoffset_x += ((($w * $this->k) - ($ow * $this->k * $svgscale_x)) / 2);
28289  break;
28290  }
28291  }
28292  // correct Y alignment
28293  switch (substr($aspect_ratio_align, 5)) {
28294  case 'Min': {
28295  // do nothing
28296  break;
28297  }
28298  case 'Max': {
28299  $svgoffset_y -= (($h * $this->k) - ($oh * $this->k * $svgscale_y));
28300  break;
28301  }
28302  default:
28303  case 'Mid': {
28304  $svgoffset_y -= ((($h * $this->k) - ($oh * $this->k * $svgscale_y)) / 2);
28305  break;
28306  }
28307  }
28308  }
28309  // store current page break mode
28310  $page_break_mode = $this->AutoPageBreak;
28311  $page_break_margin = $this->getBreakMargin();
28312  $cell_padding = $this->cell_padding;
28313  $this->SetCellPadding(0);
28314  $this->SetAutoPageBreak(false);
28315  // save the current graphic state
28316  $this->_out('q'.$this->epsmarker);
28317  // set initial clipping mask
28318  $this->Rect($x, $y, $w, $h, 'CNZ', array(), array());
28319  // scale and translate
28320  $e = $ox * $this->k * (1 - $svgscale_x);
28321  $f = ($this->h - $oy) * $this->k * (1 - $svgscale_y);
28322  $this->_out(sprintf('%F %F %F %F %F %F cm', $svgscale_x, 0, 0, $svgscale_y, ($e + $svgoffset_x), ($f + $svgoffset_y)));
28323  // creates a new XML parser to be used by the other XML functions
28324  $this->parser = xml_parser_create('UTF-8');
28325  // the following function allows to use parser inside object
28326  xml_set_object($this->parser, $this);
28327  // disable case-folding for this XML parser
28328  xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
28329  // sets the element handler functions for the XML parser
28330  xml_set_element_handler($this->parser, 'startSVGElementHandler', 'endSVGElementHandler');
28331  // sets the character data handler function for the XML parser
28332  xml_set_character_data_handler($this->parser, 'segSVGContentHandler');
28333  // start parsing an XML document
28334  if (!xml_parse($this->parser, $svgdata)) {
28335  $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));
28336  $this->Error($error_message);
28337  }
28338  // free this XML parser
28339  xml_parser_free($this->parser);
28340  // restore previous graphic state
28341  $this->_out($this->epsmarker.'Q');
28342  // restore graphic vars
28343  $this->setGraphicVars($gvars);
28344  $this->lasth = $gvars['lasth'];
28345  if (!empty($border)) {
28346  $bx = $this->x;
28347  $by = $this->y;
28348  $this->x = $ximg;
28349  if ($this->rtl) {
28350  $this->x += $w;
28351  }
28352  $this->y = $y;
28353  $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
28354  $this->x = $bx;
28355  $this->y = $by;
28356  }
28357  if ($link) {
28358  $this->Link($ximg, $y, $w, $h, $link, 0);
28359  }
28360  // set pointer to align the next text/objects
28361  switch($align) {
28362  case 'T':{
28363  $this->y = $y;
28364  $this->x = $this->img_rb_x;
28365  break;
28366  }
28367  case 'M':{
28368  $this->y = $y + round($h/2);
28369  $this->x = $this->img_rb_x;
28370  break;
28371  }
28372  case 'B':{
28373  $this->y = $this->img_rb_y;
28374  $this->x = $this->img_rb_x;
28375  break;
28376  }
28377  case 'N':{
28378  $this->SetY($this->img_rb_y);
28379  break;
28380  }
28381  default:{
28382  // restore pointer to starting position
28383  $this->x = $gvars['x'];
28384  $this->y = $gvars['y'];
28385  $this->page = $gvars['page'];
28386  $this->current_column = $gvars['current_column'];
28387  $this->tMargin = $gvars['tMargin'];
28388  $this->bMargin = $gvars['bMargin'];
28389  $this->w = $gvars['w'];
28390  $this->h = $gvars['h'];
28391  $this->wPt = $gvars['wPt'];
28392  $this->hPt = $gvars['hPt'];
28393  $this->fwPt = $gvars['fwPt'];
28394  $this->fhPt = $gvars['fhPt'];
28395  break;
28396  }
28397  }
28398  $this->endlinex = $this->img_rb_x;
28399  // restore page break
28400  $this->SetAutoPageBreak($page_break_mode, $page_break_margin);
28401  $this->cell_padding = $cell_padding;
28402  }
28403 
28412  protected function getSVGTransformMatrix($attribute) {
28413  // identity matrix
28414  $tm = array(1, 0, 0, 1, 0, 0);
28415  $transform = array();
28416  if (preg_match_all('/(matrix|translate|scale|rotate|skewX|skewY)[\s]*\(([^\)]+)\)/si', $attribute, $transform, PREG_SET_ORDER) > 0) {
28417  foreach ($transform as $key => $data) {
28418  if (!empty($data[2])) {
28419  $a = 1;
28420  $b = 0;
28421  $c = 0;
28422  $d = 1;
28423  $e = 0;
28424  $f = 0;
28425  $regs = array();
28426  switch ($data[1]) {
28427  case 'matrix': {
28428  if (preg_match('/([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)/si', $data[2], $regs)) {
28429  $a = $regs[1];
28430  $b = $regs[2];
28431  $c = $regs[3];
28432  $d = $regs[4];
28433  $e = $regs[5];
28434  $f = $regs[6];
28435  }
28436  break;
28437  }
28438  case 'translate': {
28439  if (preg_match('/([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)/si', $data[2], $regs)) {
28440  $e = $regs[1];
28441  $f = $regs[2];
28442  } elseif (preg_match('/([a-z0-9\-\.]+)/si', $data[2], $regs)) {
28443  $e = $regs[1];
28444  }
28445  break;
28446  }
28447  case 'scale': {
28448  if (preg_match('/([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)/si', $data[2], $regs)) {
28449  $a = $regs[1];
28450  $d = $regs[2];
28451  } elseif (preg_match('/([a-z0-9\-\.]+)/si', $data[2], $regs)) {
28452  $a = $regs[1];
28453  $d = $a;
28454  }
28455  break;
28456  }
28457  case 'rotate': {
28458  if (preg_match('/([0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)/si', $data[2], $regs)) {
28459  $ang = deg2rad($regs[1]);
28460  $x = $regs[2];
28461  $y = $regs[3];
28462  $a = cos($ang);
28463  $b = sin($ang);
28464  $c = -$b;
28465  $d = $a;
28466  $e = ($x * (1 - $a)) - ($y * $c);
28467  $f = ($y * (1 - $d)) - ($x * $b);
28468  } elseif (preg_match('/([0-9\-\.]+)/si', $data[2], $regs)) {
28469  $ang = deg2rad($regs[1]);
28470  $a = cos($ang);
28471  $b = sin($ang);
28472  $c = -$b;
28473  $d = $a;
28474  $e = 0;
28475  $f = 0;
28476  }
28477  break;
28478  }
28479  case 'skewX': {
28480  if (preg_match('/([0-9\-\.]+)/si', $data[2], $regs)) {
28481  $c = tan(deg2rad($regs[1]));
28482  }
28483  break;
28484  }
28485  case 'skewY': {
28486  if (preg_match('/([0-9\-\.]+)/si', $data[2], $regs)) {
28487  $b = tan(deg2rad($regs[1]));
28488  }
28489  break;
28490  }
28491  }
28492  $tm = $this->getTransformationMatrixProduct($tm, array($a, $b, $c, $d, $e, $f));
28493  }
28494  }
28495  }
28496  return $tm;
28497  }
28498 
28508  protected function getTransformationMatrixProduct($ta, $tb) {
28509  $tm = array();
28510  $tm[0] = ($ta[0] * $tb[0]) + ($ta[2] * $tb[1]);
28511  $tm[1] = ($ta[1] * $tb[0]) + ($ta[3] * $tb[1]);
28512  $tm[2] = ($ta[0] * $tb[2]) + ($ta[2] * $tb[3]);
28513  $tm[3] = ($ta[1] * $tb[2]) + ($ta[3] * $tb[3]);
28514  $tm[4] = ($ta[0] * $tb[4]) + ($ta[2] * $tb[5]) + $ta[4];
28515  $tm[5] = ($ta[1] * $tb[4]) + ($ta[3] * $tb[5]) + $ta[5];
28516  return $tm;
28517  }
28518 
28526  protected function convertSVGtMatrix($tm) {
28527  $a = $tm[0];
28528  $b = -$tm[1];
28529  $c = -$tm[2];
28530  $d = $tm[3];
28531  $e = $this->getHTMLUnitToUnits($tm[4], 1, $this->svgunit, false) * $this->k;
28532  $f = -$this->getHTMLUnitToUnits($tm[5], 1, $this->svgunit, false) * $this->k;
28533  $x = 0;
28534  $y = $this->h * $this->k;
28535  $e = ($x * (1 - $a)) - ($y * $c) + $e;
28536  $f = ($y * (1 - $d)) - ($x * $b) + $f;
28537  return array($a, $b, $c, $d, $e, $f);
28538  }
28539 
28546  protected function SVGTransform($tm) {
28547  $this->Transform($this->convertSVGtMatrix($tm));
28548  }
28549 
28565  protected function setSVGStyles($svgstyle, $prevsvgstyle, $x=0, $y=0, $w=1, $h=1, $clip_function='', $clip_params=array()) {
28566  if ($this->state != 2) {
28567  return;
28568  }
28569  $objstyle = '';
28570  $minlen = (0.01 / $this->k); // minimum acceptable length (3 point)
28571  if (!isset($svgstyle['opacity'])) {
28572  return $objstyle;
28573  }
28574  // clip-path
28575  $regs = array();
28576  if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['clip-path'], $regs)) {
28577  $clip_path = $this->svgclippaths[$regs[1]];
28578  foreach ($clip_path as $cp) {
28579  $this->startSVGElementHandler('clip-path', $cp['name'], $cp['attribs'], $cp['tm']);
28580  }
28581  }
28582  // opacity
28583  if ($svgstyle['opacity'] != 1) {
28584  $this->setAlpha($svgstyle['opacity'], 'Normal', $svgstyle['opacity'], false);
28585  }
28586  // color
28587  $fill_color = $this->convertHTMLColorToDec($svgstyle['color']);
28588  $this->SetFillColorArray($fill_color);
28589  // text color
28590  $text_color = $this->convertHTMLColorToDec($svgstyle['text-color']);
28591  $this->SetTextColorArray($text_color);
28592  // clip
28593  if (preg_match('/rect\(([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)\)/si', $svgstyle['clip'], $regs)) {
28594  $top = (isset($regs[1])?$this->getHTMLUnitToUnits($regs[1], 0, $this->svgunit, false):0);
28595  $right = (isset($regs[2])?$this->getHTMLUnitToUnits($regs[2], 0, $this->svgunit, false):0);
28596  $bottom = (isset($regs[3])?$this->getHTMLUnitToUnits($regs[3], 0, $this->svgunit, false):0);
28597  $left = (isset($regs[4])?$this->getHTMLUnitToUnits($regs[4], 0, $this->svgunit, false):0);
28598  $cx = $x + $left;
28599  $cy = $y + $top;
28600  $cw = $w - $left - $right;
28601  $ch = $h - $top - $bottom;
28602  if ($svgstyle['clip-rule'] == 'evenodd') {
28603  $clip_rule = 'CNZ';
28604  } else {
28605  $clip_rule = 'CEO';
28606  }
28607  $this->Rect($cx, $cy, $cw, $ch, $clip_rule, array(), array());
28608  }
28609  // fill
28610  $regs = array();
28611  if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['fill'], $regs)) {
28612  // gradient
28613  $gradient = $this->svggradients[$regs[1]];
28614  if (isset($gradient['xref'])) {
28615  // reference to another gradient definition
28616  $newgradient = $this->svggradients[$gradient['xref']];
28617  $newgradient['coords'] = $gradient['coords'];
28618  $newgradient['mode'] = $gradient['mode'];
28619  $newgradient['gradientUnits'] = $gradient['gradientUnits'];
28620  if (isset($gradient['gradientTransform'])) {
28621  $newgradient['gradientTransform'] = $gradient['gradientTransform'];
28622  }
28623  $gradient = $newgradient;
28624  }
28625  //save current Graphic State
28626  $this->_out('q');
28627  //set clipping area
28628  if (!empty($clip_function) AND method_exists($this, $clip_function)) {
28629  $bbox = call_user_func_array(array($this, $clip_function), $clip_params);
28630  if (is_array($bbox) AND (count($bbox) == 4)) {
28631  list($x, $y, $w, $h) = $bbox;
28632  }
28633  }
28634  if ($gradient['mode'] == 'measure') {
28635  if (isset($gradient['gradientTransform']) AND !empty($gradient['gradientTransform'])) {
28636  $gtm = $gradient['gradientTransform'];
28637  // apply transformation matrix
28638  $xa = ($gtm[0] * $gradient['coords'][0]) + ($gtm[2] * $gradient['coords'][1]) + $gtm[4];
28639  $ya = ($gtm[1] * $gradient['coords'][0]) + ($gtm[3] * $gradient['coords'][1]) + $gtm[5];
28640  $xb = ($gtm[0] * $gradient['coords'][2]) + ($gtm[2] * $gradient['coords'][3]) + $gtm[4];
28641  $yb = ($gtm[1] * $gradient['coords'][2]) + ($gtm[3] * $gradient['coords'][3]) + $gtm[5];
28642  if (isset($gradient['coords'][4])) {
28643  $gradient['coords'][4] = sqrt(pow(($gtm[0] * $gradient['coords'][4]), 2) + pow(($gtm[1] * $gradient['coords'][4]), 2));
28644  }
28645  $gradient['coords'][0] = $xa;
28646  $gradient['coords'][1] = $ya;
28647  $gradient['coords'][2] = $xb;
28648  $gradient['coords'][3] = $yb;
28649  }
28650  // convert SVG coordinates to user units
28651  $gradient['coords'][0] = $this->getHTMLUnitToUnits($gradient['coords'][0], 0, $this->svgunit, false);
28652  $gradient['coords'][1] = $this->getHTMLUnitToUnits($gradient['coords'][1], 0, $this->svgunit, false);
28653  $gradient['coords'][2] = $this->getHTMLUnitToUnits($gradient['coords'][2], 0, $this->svgunit, false);
28654  $gradient['coords'][3] = $this->getHTMLUnitToUnits($gradient['coords'][3], 0, $this->svgunit, false);
28655  if (isset($gradient['coords'][4])) {
28656  $gradient['coords'][4] = $this->getHTMLUnitToUnits($gradient['coords'][4], 0, $this->svgunit, false);
28657  }
28658  if ($w <= $minlen) {
28659  $w = $minlen;
28660  }
28661  if ($h <= $minlen) {
28662  $h = $minlen;
28663  }
28664  // shift units
28665  if ($gradient['gradientUnits'] == 'objectBoundingBox') {
28666  // convert to SVG coordinate system
28667  $gradient['coords'][0] += $x;
28668  $gradient['coords'][1] += $y;
28669  $gradient['coords'][2] += $x;
28670  $gradient['coords'][3] += $y;
28671  }
28672  // calculate percentages
28673  $gradient['coords'][0] = (($gradient['coords'][0] - $x) / $w);
28674  $gradient['coords'][1] = (($gradient['coords'][1] - $y) / $h);
28675  $gradient['coords'][2] = (($gradient['coords'][2] - $x) / $w);
28676  $gradient['coords'][3] = (($gradient['coords'][3] - $y) / $h);
28677  if (isset($gradient['coords'][4])) {
28678  $gradient['coords'][4] /= $w;
28679  }
28680  } elseif ($gradient['mode'] == 'percentage') {
28681  foreach($gradient['coords'] as $key => $val) {
28682  $gradient['coords'][$key] = (intval($val) / 100);
28683  if ($val < 0) {
28684  $gradient['coords'][$key] = 0;
28685  } elseif ($val > 1) {
28686  $gradient['coords'][$key] = 1;
28687  }
28688  }
28689  }
28690  if (($gradient['type'] == 2) AND ($gradient['coords'][0] == $gradient['coords'][2]) AND ($gradient['coords'][1] == $gradient['coords'][3])) {
28691  // single color (no shading)
28692  $gradient['coords'][0] = 1;
28693  $gradient['coords'][1] = 0;
28694  $gradient['coords'][2] = 0.999;
28695  $gradient['coords'][3] = 0;
28696  }
28697  // swap Y coordinates
28698  $tmp = $gradient['coords'][1];
28699  $gradient['coords'][1] = $gradient['coords'][3];
28700  $gradient['coords'][3] = $tmp;
28701  // set transformation map for gradient
28702  if ($gradient['type'] == 3) {
28703  // circular gradient
28704  $cy = $this->h - $y - ($gradient['coords'][1] * ($w + $h));
28705  $this->_out(sprintf('%F 0 0 %F %F %F cm', ($w * $this->k), ($w * $this->k), ($x * $this->k), ($cy * $this->k)));
28706  } else {
28707  $this->_out(sprintf('%F 0 0 %F %F %F cm', ($w * $this->k), ($h * $this->k), ($x * $this->k), (($this->h - ($y + $h)) * $this->k)));
28708  }
28709  if (count($gradient['stops']) > 1) {
28710  $this->Gradient($gradient['type'], $gradient['coords'], $gradient['stops'], array(), false);
28711  }
28712  } elseif ($svgstyle['fill'] != 'none') {
28713  $fill_color = $this->convertHTMLColorToDec($svgstyle['fill']);
28714  if ($svgstyle['fill-opacity'] != 1) {
28715  $this->setAlpha($this->alpha['CA'], 'Normal', $svgstyle['fill-opacity'], false);
28716  }
28717  $this->SetFillColorArray($fill_color);
28718  if ($svgstyle['fill-rule'] == 'evenodd') {
28719  $objstyle .= 'F*';
28720  } else {
28721  $objstyle .= 'F';
28722  }
28723  }
28724  // stroke
28725  if ($svgstyle['stroke'] != 'none') {
28726  if ($svgstyle['stroke-opacity'] != 1) {
28727  $this->setAlpha($svgstyle['stroke-opacity'], 'Normal', $this->alpha['ca'], false);
28728  }
28729  $stroke_style = array(
28730  'color' => $this->convertHTMLColorToDec($svgstyle['stroke']),
28731  'width' => $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false),
28732  'cap' => $svgstyle['stroke-linecap'],
28733  'join' => $svgstyle['stroke-linejoin']
28734  );
28735  if (isset($svgstyle['stroke-dasharray']) AND !empty($svgstyle['stroke-dasharray']) AND ($svgstyle['stroke-dasharray'] != 'none')) {
28736  $stroke_style['dash'] = $svgstyle['stroke-dasharray'];
28737  }
28738  $this->SetLineStyle($stroke_style);
28739  $objstyle .= 'D';
28740  }
28741  // font
28742  $regs = array();
28743  if (!empty($svgstyle['font'])) {
28744  if (preg_match('/font-family[\s]*:[\s]*([^\;\"]*)/si', $svgstyle['font'], $regs)) {
28745  $font_family = $this->getFontFamilyName($regs[1]);
28746  } else {
28747  $font_family = $svgstyle['font-family'];
28748  }
28749  if (preg_match('/font-size[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
28750  $font_size = trim($regs[1]);
28751  } else {
28752  $font_size = $svgstyle['font-size'];
28753  }
28754  if (preg_match('/font-style[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
28755  $font_style = trim($regs[1]);
28756  } else {
28757  $font_style = $svgstyle['font-style'];
28758  }
28759  if (preg_match('/font-weight[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
28760  $font_weight = trim($regs[1]);
28761  } else {
28762  $font_weight = $svgstyle['font-weight'];
28763  }
28764  if (preg_match('/font-stretch[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
28765  $font_stretch = trim($regs[1]);
28766  } else {
28767  $font_stretch = $svgstyle['font-stretch'];
28768  }
28769  if (preg_match('/letter-spacing[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
28770  $font_spacing = trim($regs[1]);
28771  } else {
28772  $font_spacing = $svgstyle['letter-spacing'];
28773  }
28774  } else {
28775  $font_family = $this->getFontFamilyName($svgstyle['font-family']);
28776  $font_size = $svgstyle['font-size'];
28777  $font_style = $svgstyle['font-style'];
28778  $font_weight = $svgstyle['font-weight'];
28779  $font_stretch = $svgstyle['font-stretch'];
28780  $font_spacing = $svgstyle['letter-spacing'];
28781  }
28782  $font_size = $this->getHTMLUnitToUnits($font_size, $prevsvgstyle['font-size'], $this->svgunit, false) * $this->k;
28783  $font_stretch = $this->getCSSFontStretching($font_stretch, $svgstyle['font-stretch']);
28784  $font_spacing = $this->getCSSFontSpacing($font_spacing, $svgstyle['letter-spacing']);
28785  switch ($font_style) {
28786  case 'italic': {
28787  $font_style = 'I';
28788  break;
28789  }
28790  case 'oblique': {
28791  $font_style = 'I';
28792  break;
28793  }
28794  default:
28795  case 'normal': {
28796  $font_style = '';
28797  break;
28798  }
28799  }
28800  switch ($font_weight) {
28801  case 'bold':
28802  case 'bolder': {
28803  $font_style .= 'B';
28804  break;
28805  }
28806  }
28807  switch ($svgstyle['text-decoration']) {
28808  case 'underline': {
28809  $font_style .= 'U';
28810  break;
28811  }
28812  case 'overline': {
28813  $font_style .= 'O';
28814  break;
28815  }
28816  case 'line-through': {
28817  $font_style .= 'D';
28818  break;
28819  }
28820  default:
28821  case 'none': {
28822  break;
28823  }
28824  }
28825  $this->SetFont($font_family, $font_style, $font_size);
28826  $this->setFontStretching($font_stretch);
28827  $this->setFontSpacing($font_spacing);
28828  return $objstyle;
28829  }
28830 
28849  protected function SVGPath($d, $style='') {
28850  if ($this->state != 2) {
28851  return;
28852  }
28853  // set fill/stroke style
28854  $op = $this->getPathPaintOperator($style, '');
28855  if (empty($op)) {
28856  return;
28857  }
28858  $paths = array();
28859  $d = preg_replace('/([0-9ACHLMQSTVZ])([\-\+])/si', '\\1 \\2', $d);
28860  preg_match_all('/([ACHLMQSTVZ])[\s]*([^ACHLMQSTVZ\"]*)/si', $d, $paths, PREG_SET_ORDER);
28861  $x = 0;
28862  $y = 0;
28863  $x1 = 0;
28864  $y1 = 0;
28865  $x2 = 0;
28866  $y2 = 0;
28867  $xmin = 2147483647;
28868  $xmax = 0;
28869  $ymin = 2147483647;
28870  $ymax = 0;
28871  $relcoord = false;
28872  $minlen = (0.01 / $this->k); // minimum acceptable length (3 point)
28873  $firstcmd = true; // used to print first point
28874  // draw curve pieces
28875  foreach ($paths as $key => $val) {
28876  // get curve type
28877  $cmd = trim($val[1]);
28878  if (strtolower($cmd) == $cmd) {
28879  // use relative coordinated instead of absolute
28880  $relcoord = true;
28881  $xoffset = $x;
28882  $yoffset = $y;
28883  } else {
28884  $relcoord = false;
28885  $xoffset = 0;
28886  $yoffset = 0;
28887  }
28888  $params = array();
28889  if (isset($val[2])) {
28890  // get curve parameters
28891  $rawparams = preg_split('/([\,\s]+)/si', trim($val[2]));
28892  $params = array();
28893  foreach ($rawparams as $ck => $cp) {
28894  $params[$ck] = $this->getHTMLUnitToUnits($cp, 0, $this->svgunit, false);
28895  if (abs($params[$ck]) < $minlen) {
28896  // aproximate little values to zero
28897  $params[$ck] = 0;
28898  }
28899  }
28900  }
28901  // store current origin point
28902  $x0 = $x;
28903  $y0 = $y;
28904  switch (strtoupper($cmd)) {
28905  case 'M': { // moveto
28906  foreach ($params as $ck => $cp) {
28907  if (($ck % 2) == 0) {
28908  $x = $cp + $xoffset;
28909  } else {
28910  $y = $cp + $yoffset;
28911  if ($firstcmd OR (abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
28912  if ($ck == 1) {
28913  $this->_outPoint($x, $y);
28914  $firstcmd = false;
28915  } else {
28916  $this->_outLine($x, $y);
28917  }
28918  $x0 = $x;
28919  $y0 = $y;
28920  }
28921  $xmin = min($xmin, $x);
28922  $ymin = min($ymin, $y);
28923  $xmax = max($xmax, $x);
28924  $ymax = max($ymax, $y);
28925  if ($relcoord) {
28926  $xoffset = $x;
28927  $yoffset = $y;
28928  }
28929  }
28930  }
28931  break;
28932  }
28933  case 'L': { // lineto
28934  foreach ($params as $ck => $cp) {
28935  if (($ck % 2) == 0) {
28936  $x = $cp + $xoffset;
28937  } else {
28938  $y = $cp + $yoffset;
28939  if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
28940  $this->_outLine($x, $y);
28941  $x0 = $x;
28942  $y0 = $y;
28943  }
28944  $xmin = min($xmin, $x);
28945  $ymin = min($ymin, $y);
28946  $xmax = max($xmax, $x);
28947  $ymax = max($ymax, $y);
28948  if ($relcoord) {
28949  $xoffset = $x;
28950  $yoffset = $y;
28951  }
28952  }
28953  }
28954  break;
28955  }
28956  case 'H': { // horizontal lineto
28957  foreach ($params as $ck => $cp) {
28958  $x = $cp + $xoffset;
28959  if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
28960  $this->_outLine($x, $y);
28961  $x0 = $x;
28962  $y0 = $y;
28963  }
28964  $xmin = min($xmin, $x);
28965  $xmax = max($xmax, $x);
28966  if ($relcoord) {
28967  $xoffset = $x;
28968  }
28969  }
28970  break;
28971  }
28972  case 'V': { // vertical lineto
28973  foreach ($params as $ck => $cp) {
28974  $y = $cp + $yoffset;
28975  if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
28976  $this->_outLine($x, $y);
28977  $x0 = $x;
28978  $y0 = $y;
28979  }
28980  $ymin = min($ymin, $y);
28981  $ymax = max($ymax, $y);
28982  if ($relcoord) {
28983  $yoffset = $y;
28984  }
28985  }
28986  break;
28987  }
28988  case 'C': { // curveto
28989  foreach ($params as $ck => $cp) {
28990  $params[$ck] = $cp;
28991  if ((($ck + 1) % 6) == 0) {
28992  $x1 = $params[($ck - 5)] + $xoffset;
28993  $y1 = $params[($ck - 4)] + $yoffset;
28994  $x2 = $params[($ck - 3)] + $xoffset;
28995  $y2 = $params[($ck - 2)] + $yoffset;
28996  $x = $params[($ck - 1)] + $xoffset;
28997  $y = $params[($ck)] + $yoffset;
28998  $this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
28999  $xmin = min($xmin, $x, $x1, $x2);
29000  $ymin = min($ymin, $y, $y1, $y2);
29001  $xmax = max($xmax, $x, $x1, $x2);
29002  $ymax = max($ymax, $y, $y1, $y2);
29003  if ($relcoord) {
29004  $xoffset = $x;
29005  $yoffset = $y;
29006  }
29007  }
29008  }
29009  break;
29010  }
29011  case 'S': { // shorthand/smooth curveto
29012  foreach ($params as $ck => $cp) {
29013  $params[$ck] = $cp;
29014  if ((($ck + 1) % 4) == 0) {
29015  if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'C') OR (strtoupper($paths[($key - 1)][1]) == 'S'))) {
29016  $x1 = (2 * $x) - $x2;
29017  $y1 = (2 * $y) - $y2;
29018  } else {
29019  $x1 = $x;
29020  $y1 = $y;
29021  }
29022  $x2 = $params[($ck - 3)] + $xoffset;
29023  $y2 = $params[($ck - 2)] + $yoffset;
29024  $x = $params[($ck - 1)] + $xoffset;
29025  $y = $params[($ck)] + $yoffset;
29026  $this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
29027  $xmin = min($xmin, $x, $x1, $x2);
29028  $ymin = min($ymin, $y, $y1, $y2);
29029  $xmax = max($xmax, $x, $x1, $x2);
29030  $ymax = max($ymax, $y, $y1, $y2);
29031  if ($relcoord) {
29032  $xoffset = $x;
29033  $yoffset = $y;
29034  }
29035  }
29036  }
29037  break;
29038  }
29039  case 'Q': { // quadratic Bézier curveto
29040  foreach ($params as $ck => $cp) {
29041  $params[$ck] = $cp;
29042  if ((($ck + 1) % 4) == 0) {
29043  // convert quadratic points to cubic points
29044  $x1 = $params[($ck - 3)] + $xoffset;
29045  $y1 = $params[($ck - 2)] + $yoffset;
29046  $xa = ($x + (2 * $x1)) / 3;
29047  $ya = ($y + (2 * $y1)) / 3;
29048  $x = $params[($ck - 1)] + $xoffset;
29049  $y = $params[($ck)] + $yoffset;
29050  $xb = ($x + (2 * $x1)) / 3;
29051  $yb = ($y + (2 * $y1)) / 3;
29052  $this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
29053  $xmin = min($xmin, $x, $xa, $xb);
29054  $ymin = min($ymin, $y, $ya, $yb);
29055  $xmax = max($xmax, $x, $xa, $xb);
29056  $ymax = max($ymax, $y, $ya, $yb);
29057  if ($relcoord) {
29058  $xoffset = $x;
29059  $yoffset = $y;
29060  }
29061  }
29062  }
29063  break;
29064  }
29065  case 'T': { // shorthand/smooth quadratic Bézier curveto
29066  foreach ($params as $ck => $cp) {
29067  $params[$ck] = $cp;
29068  if (($ck % 2) != 0) {
29069  if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'Q') OR (strtoupper($paths[($key - 1)][1]) == 'T'))) {
29070  $x1 = (2 * $x) - $x1;
29071  $y1 = (2 * $y) - $y1;
29072  } else {
29073  $x1 = $x;
29074  $y1 = $y;
29075  }
29076  // convert quadratic points to cubic points
29077  $xa = ($x + (2 * $x1)) / 3;
29078  $ya = ($y + (2 * $y1)) / 3;
29079  $x = $params[($ck - 1)] + $xoffset;
29080  $y = $params[($ck)] + $yoffset;
29081  $xb = ($x + (2 * $x1)) / 3;
29082  $yb = ($y + (2 * $y1)) / 3;
29083  $this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
29084  $xmin = min($xmin, $x, $xa, $xb);
29085  $ymin = min($ymin, $y, $ya, $yb);
29086  $xmax = max($xmax, $x, $xa, $xb);
29087  $ymax = max($ymax, $y, $ya, $yb);
29088  if ($relcoord) {
29089  $xoffset = $x;
29090  $yoffset = $y;
29091  }
29092  }
29093  }
29094  break;
29095  }
29096  case 'A': { // elliptical arc
29097  foreach ($params as $ck => $cp) {
29098  $params[$ck] = $cp;
29099  if ((($ck + 1) % 7) == 0) {
29100  $x0 = $x;
29101  $y0 = $y;
29102  $rx = abs($params[($ck - 6)]);
29103  $ry = abs($params[($ck - 5)]);
29104  $ang = -$rawparams[($ck - 4)];
29105  $angle = deg2rad($ang);
29106  $fa = $rawparams[($ck - 3)]; // large-arc-flag
29107  $fs = $rawparams[($ck - 2)]; // sweep-flag
29108  $x = $params[($ck - 1)] + $xoffset;
29109  $y = $params[$ck] + $yoffset;
29110  if ((abs($x0 - $x) < $minlen) AND (abs($y0 - $y) < $minlen)) {
29111  // endpoints are almost identical
29112  $xmin = min($xmin, $x);
29113  $ymin = min($ymin, $y);
29114  $xmax = max($xmax, $x);
29115  $ymax = max($ymax, $y);
29116  } else {
29117  $cos_ang = cos($angle);
29118  $sin_ang = sin($angle);
29119  $a = (($x0 - $x) / 2);
29120  $b = (($y0 - $y) / 2);
29121  $xa = ($a * $cos_ang) - ($b * $sin_ang);
29122  $ya = ($a * $sin_ang) + ($b * $cos_ang);
29123  $rx2 = $rx * $rx;
29124  $ry2 = $ry * $ry;
29125  $xa2 = $xa * $xa;
29126  $ya2 = $ya * $ya;
29127  $delta = ($xa2 / $rx2) + ($ya2 / $ry2);
29128  if ($delta > 1) {
29129  $rx *= sqrt($delta);
29130  $ry *= sqrt($delta);
29131  $rx2 = $rx * $rx;
29132  $ry2 = $ry * $ry;
29133  }
29134  $numerator = (($rx2 * $ry2) - ($rx2 * $ya2) - ($ry2 * $xa2));
29135  if ($numerator < 0) {
29136  $root = 0;
29137  } else {
29138  $root = sqrt($numerator / (($rx2 * $ya2) + ($ry2 * $xa2)));
29139  }
29140  if ($fa == $fs){
29141  $root *= -1;
29142  }
29143  $cax = $root * (($rx * $ya) / $ry);
29144  $cay = -$root * (($ry * $xa) / $rx);
29145  // coordinates of ellipse center
29146  $cx = ($cax * $cos_ang) - ($cay * $sin_ang) + (($x0 + $x) / 2);
29147  $cy = ($cax * $sin_ang) + ($cay * $cos_ang) + (($y0 + $y) / 2);
29148  // get angles
29149  $angs = $this->getVectorsAngle(1, 0, (($xa - $cax) / $rx), (($cay - $ya) / $ry));
29150  $dang = $this->getVectorsAngle((($xa - $cax) / $rx), (($ya - $cay) / $ry), ((-$xa - $cax) / $rx), ((-$ya - $cay) / $ry));
29151  if (($fs == 0) AND ($dang > 0)) {
29152  $dang -= (2 * M_PI);
29153  } elseif (($fs == 1) AND ($dang < 0)) {
29154  $dang += (2 * M_PI);
29155  }
29156  $angf = $angs - $dang;
29157  if ((($fs == 0) AND ($angs > $angf)) OR (($fs == 1) AND ($angs < $angf))) {
29158  // reverse angles
29159  $tmp = $angs;
29160  $angs = $angf;
29161  $angf = $tmp;
29162  }
29163  $angs = round(rad2deg($angs), 6);
29164  $angf = round(rad2deg($angf), 6);
29165  // covent angles to positive values
29166  if (($angs < 0) AND ($angf < 0)) {
29167  $angs += 360;
29168  $angf += 360;
29169  }
29170  $pie = false;
29171  if (($key == 0) AND (isset($paths[($key + 1)][1])) AND (trim($paths[($key + 1)][1]) == 'z')) {
29172  $pie = true;
29173  }
29174  list($axmin, $aymin, $axmax, $aymax) = $this->_outellipticalarc($cx, $cy, $rx, $ry, $ang, $angs, $angf, $pie, 2, false, ($fs == 0), true);
29175  $xmin = min($xmin, $x, $axmin);
29176  $ymin = min($ymin, $y, $aymin);
29177  $xmax = max($xmax, $x, $axmax);
29178  $ymax = max($ymax, $y, $aymax);
29179  }
29180  if ($relcoord) {
29181  $xoffset = $x;
29182  $yoffset = $y;
29183  }
29184  }
29185  }
29186  break;
29187  }
29188  case 'Z': {
29189  $this->_out('h');
29190  break;
29191  }
29192  }
29193  $firstcmd = false;
29194  } // end foreach
29195  if (!empty($op)) {
29196  $this->_out($op);
29197  }
29198  return array($xmin, $ymin, ($xmax - $xmin), ($ymax - $ymin));
29199  }
29200 
29211  protected function getVectorsAngle($x1, $y1, $x2, $y2) {
29212  $dprod = ($x1 * $x2) + ($y1 * $y2);
29213  $dist1 = sqrt(($x1 * $x1) + ($y1 * $y1));
29214  $dist2 = sqrt(($x2 * $x2) + ($y2 * $y2));
29215  $angle = acos($dprod / ($dist1 * $dist2));
29216  if (is_nan($angle)) {
29217  $angle = M_PI;
29218  }
29219  if ((($x1 * $y2) - ($x2 * $y1)) < 0) {
29220  $angle *= -1;
29221  }
29222  return $angle;
29223  }
29224 
29235  protected function startSVGElementHandler($parser, $name, $attribs, $ctm=array()) {
29236  // check if we are in clip mode
29237  if ($this->svgclipmode) {
29238  $this->svgclippaths[$this->svgclipid][] = array('name' => $name, 'attribs' => $attribs, 'tm' => $this->svgcliptm[$this->svgclipid]);
29239  return;
29240  }
29241  if ($this->svgdefsmode AND !in_array($name, array('clipPath', 'linearGradient', 'radialGradient', 'stop'))) {
29242  if (!isset($attribs['id'])) {
29243  $attribs['id'] = 'DF_'.(count($this->svgdefs) + 1);
29244  }
29245  $this->svgdefs[$attribs['id']] = array('name' => $name, 'attribs' => $attribs);
29246  return;
29247  }
29248  $clipping = false;
29249  if ($parser == 'clip-path') {
29250  // set clipping mode
29251  $clipping = true;
29252  }
29253  // get styling properties
29254  $prev_svgstyle = $this->svgstyles[(count($this->svgstyles) - 1)]; // previous style
29255  $svgstyle = $this->svgstyles[0]; // set default style
29256  if ($clipping AND !isset($attribs['fill']) AND (!isset($attribs['style']) OR (!preg_match('/[;\"\s]{1}fill[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval)))) {
29257  // default fill attribute for clipping
29258  $attribs['fill'] = 'none';
29259  }
29260  if (isset($attribs['style']) AND !$this->empty_string($attribs['style'])) {
29261  // fix style for regular expression
29262  $attribs['style'] = ';'.$attribs['style'];
29263  }
29264  foreach ($prev_svgstyle as $key => $val) {
29265  if (in_array($key, $this->svginheritprop)) {
29266  // inherit previous value
29267  $svgstyle[$key] = $val;
29268  }
29269  if (isset($attribs[$key]) AND !$this->empty_string($attribs[$key])) {
29270  // specific attribute settings
29271  if ($attribs[$key] == 'inherit') {
29272  $svgstyle[$key] = $val;
29273  } else {
29274  $svgstyle[$key] = $attribs[$key];
29275  }
29276  } elseif (isset($attribs['style']) AND !$this->empty_string($attribs['style'])) {
29277  // CSS style syntax
29278  $attrval = array();
29279  if (preg_match('/[;\"\s]{1}'.$key.'[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval) AND isset($attrval[1])) {
29280  if ($attrval[1] == 'inherit') {
29281  $svgstyle[$key] = $val;
29282  } else {
29283  $svgstyle[$key] = $attrval[1];
29284  }
29285  }
29286  }
29287  }
29288  // transformation matrix
29289  if (!empty($ctm)) {
29290  $tm = $ctm;
29291  } else {
29292  //$tm = $this->svgstyles[(count($this->svgstyles) - 1)]['transfmatrix'];
29293  $tm = array(1,0,0,1,0,0);
29294  }
29295  if (isset($attribs['transform']) AND !empty($attribs['transform'])) {
29296  $tm = $this->getTransformationMatrixProduct($tm, $this->getSVGTransformMatrix($attribs['transform']));
29297  }
29298  $svgstyle['transfmatrix'] = $tm;
29299  $invisible = false;
29300  if (($svgstyle['visibility'] == 'hidden') OR ($svgstyle['visibility'] == 'collapse') OR ($svgstyle['display'] == 'none')) {
29301  // the current graphics element is invisible (nothing is painted)
29302  $invisible = true;
29303  }
29304  // process tag
29305  switch($name) {
29306  case 'defs': {
29307  $this->svgdefsmode = true;
29308  break;
29309  }
29310  // clipPath
29311  case 'clipPath': {
29312  if ($invisible) {
29313  break;
29314  }
29315  $this->svgclipmode = true;
29316  if (!isset($attribs['id'])) {
29317  $attribs['id'] = 'CP_'.(count($this->svgcliptm) + 1);
29318  }
29319  $this->svgclipid = $attribs['id'];
29320  $this->svgclippaths[$this->svgclipid] = array();
29321  $this->svgcliptm[$this->svgclipid] = $tm;
29322  break;
29323  }
29324  case 'svg': {
29325  // start of SVG object
29326  break;
29327  }
29328  case 'g': {
29329  // group together related graphics elements
29330  array_push($this->svgstyles, $svgstyle);
29331  $this->StartTransform();
29332  $this->SVGTransform($tm);
29333  $this->setSVGStyles($svgstyle, $prev_svgstyle);
29334  break;
29335  }
29336  case 'linearGradient': {
29337  if ($this->pdfa_mode) {
29338  break;
29339  }
29340  if (!isset($attribs['id'])) {
29341  $attribs['id'] = 'GR_'.(count($this->svggradients) + 1);
29342  }
29343  $this->svggradientid = $attribs['id'];
29344  $this->svggradients[$this->svggradientid] = array();
29345  $this->svggradients[$this->svggradientid]['type'] = 2;
29346  $this->svggradients[$this->svggradientid]['stops'] = array();
29347  if (isset($attribs['gradientUnits'])) {
29348  $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits'];
29349  } else {
29350  $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox';
29351  }
29352  //$attribs['spreadMethod']
29353  if (((!isset($attribs['x1'])) AND (!isset($attribs['y1'])) AND (!isset($attribs['x2'])) AND (!isset($attribs['y2'])))
29354  OR ((isset($attribs['x1']) AND (substr($attribs['x1'], -1) == '%'))
29355  OR (isset($attribs['y1']) AND (substr($attribs['y1'], -1) == '%'))
29356  OR (isset($attribs['x2']) AND (substr($attribs['x2'], -1) == '%'))
29357  OR (isset($attribs['y2']) AND (substr($attribs['y2'], -1) == '%')))) {
29358  $this->svggradients[$this->svggradientid]['mode'] = 'percentage';
29359  } else {
29360  $this->svggradients[$this->svggradientid]['mode'] = 'measure';
29361  }
29362  $x1 = (isset($attribs['x1'])?$attribs['x1']:'0');
29363  $y1 = (isset($attribs['y1'])?$attribs['y1']:'0');
29364  $x2 = (isset($attribs['x2'])?$attribs['x2']:'100');
29365  $y2 = (isset($attribs['y2'])?$attribs['y2']:'0');
29366  if (isset($attribs['gradientTransform'])) {
29367  $this->svggradients[$this->svggradientid]['gradientTransform'] = $this->getSVGTransformMatrix($attribs['gradientTransform']);
29368  }
29369  $this->svggradients[$this->svggradientid]['coords'] = array($x1, $y1, $x2, $y2);
29370  if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
29371  // gradient is defined on another place
29372  $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1);
29373  }
29374  break;
29375  }
29376  case 'radialGradient': {
29377  if ($this->pdfa_mode) {
29378  break;
29379  }
29380  if (!isset($attribs['id'])) {
29381  $attribs['id'] = 'GR_'.(count($this->svggradients) + 1);
29382  }
29383  $this->svggradientid = $attribs['id'];
29384  $this->svggradients[$this->svggradientid] = array();
29385  $this->svggradients[$this->svggradientid]['type'] = 3;
29386  $this->svggradients[$this->svggradientid]['stops'] = array();
29387  if (isset($attribs['gradientUnits'])) {
29388  $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits'];
29389  } else {
29390  $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox';
29391  }
29392  //$attribs['spreadMethod']
29393  if (((!isset($attribs['cx'])) AND (!isset($attribs['cy'])))
29394  OR ((isset($attribs['cx']) AND (substr($attribs['cx'], -1) == '%'))
29395  OR (isset($attribs['cy']) AND (substr($attribs['cy'], -1) == '%')) )) {
29396  $this->svggradients[$this->svggradientid]['mode'] = 'percentage';
29397  } else {
29398  $this->svggradients[$this->svggradientid]['mode'] = 'measure';
29399  }
29400  $cx = (isset($attribs['cx']) ? $attribs['cx'] : 0.5);
29401  $cy = (isset($attribs['cy']) ? $attribs['cy'] : 0.5);
29402  $fx = (isset($attribs['fx']) ? $attribs['fx'] : $cx);
29403  $fy = (isset($attribs['fy']) ? $attribs['fy'] : $cy);
29404  $r = (isset($attribs['r']) ? $attribs['r'] : 0.5);
29405  if (isset($attribs['gradientTransform'])) {
29406  $this->svggradients[$this->svggradientid]['gradientTransform'] = $this->getSVGTransformMatrix($attribs['gradientTransform']);
29407  }
29408  $this->svggradients[$this->svggradientid]['coords'] = array($cx, $cy, $fx, $fy, $r);
29409  if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
29410  // gradient is defined on another place
29411  $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1);
29412  }
29413  break;
29414  }
29415  case 'stop': {
29416  // gradient stops
29417  if (substr($attribs['offset'], -1) == '%') {
29418  $offset = floatval(substr($attribs['offset'], -1)) / 100;
29419  } else {
29420  $offset = floatval($attribs['offset']);
29421  if ($offset > 1) {
29422  $offset /= 100;
29423  }
29424  }
29425  $stop_color = isset($svgstyle['stop-color'])?$this->convertHTMLColorToDec($svgstyle['stop-color']):'black';
29426  $opacity = isset($svgstyle['stop-opacity'])?$svgstyle['stop-opacity']:1;
29427  $this->svggradients[$this->svggradientid]['stops'][] = array('offset' => $offset, 'color' => $stop_color, 'opacity' => $opacity);
29428  break;
29429  }
29430  // paths
29431  case 'path': {
29432  if ($invisible) {
29433  break;
29434  }
29435  if (isset($attribs['d'])) {
29436  $d = trim($attribs['d']);
29437  if (!empty($d)) {
29438  if ($clipping) {
29439  $this->SVGTransform($tm);
29440  $this->SVGPath($d, 'CNZ');
29441  } else {
29442  $this->StartTransform();
29443  $this->SVGTransform($tm);
29444  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, 0, 0, 1, 1, 'SVGPath', array($d, 'CNZ'));
29445  if (!empty($obstyle)) {
29446  $this->SVGPath($d, $obstyle);
29447  }
29448  $this->StopTransform();
29449  }
29450  }
29451  }
29452  break;
29453  }
29454  // shapes
29455  case 'rect': {
29456  if ($invisible) {
29457  break;
29458  }
29459  $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0);
29460  $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0);
29461  $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0);
29462  $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0);
29463  $rx = (isset($attribs['rx'])?$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false):0);
29464  $ry = (isset($attribs['ry'])?$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false):$rx);
29465  if ($clipping) {
29466  $this->SVGTransform($tm);
29467  $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ', array(), array());
29468  } else {
29469  $this->StartTransform();
29470  $this->SVGTransform($tm);
29471  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'RoundedRectXY', array($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ'));
29472  if (!empty($obstyle)) {
29473  $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', $obstyle, array(), array());
29474  }
29475  $this->StopTransform();
29476  }
29477  break;
29478  }
29479  case 'circle': {
29480  if ($invisible) {
29481  break;
29482  }
29483  $r = (isset($attribs['r']) ? $this->getHTMLUnitToUnits($attribs['r'], 0, $this->svgunit, false) : 0);
29484  $cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0));
29485  $cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0));
29486  $x = ($cx - $r);
29487  $y = ($cy - $r);
29488  $w = (2 * $r);
29489  $h = $w;
29490  if ($clipping) {
29491  $this->SVGTransform($tm);
29492  $this->Circle($cx, $cy, $r, 0, 360, 'CNZ', array(), array(), 8);
29493  } else {
29494  $this->StartTransform();
29495  $this->SVGTransform($tm);
29496  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Circle', array($cx, $cy, $r, 0, 360, 'CNZ'));
29497  if (!empty($obstyle)) {
29498  $this->Circle($cx, $cy, $r, 0, 360, $obstyle, array(), array(), 8);
29499  }
29500  $this->StopTransform();
29501  }
29502  break;
29503  }
29504  case 'ellipse': {
29505  if ($invisible) {
29506  break;
29507  }
29508  $rx = (isset($attribs['rx']) ? $this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false) : 0);
29509  $ry = (isset($attribs['ry']) ? $this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false) : 0);
29510  $cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0));
29511  $cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0));
29512  $x = ($cx - $rx);
29513  $y = ($cy - $ry);
29514  $w = (2 * $rx);
29515  $h = (2 * $ry);
29516  if ($clipping) {
29517  $this->SVGTransform($tm);
29518  $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ', array(), array(), 8);
29519  } else {
29520  $this->StartTransform();
29521  $this->SVGTransform($tm);
29522  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Ellipse', array($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ'));
29523  if (!empty($obstyle)) {
29524  $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, $obstyle, array(), array(), 8);
29525  }
29526  $this->StopTransform();
29527  }
29528  break;
29529  }
29530  case 'line': {
29531  if ($invisible) {
29532  break;
29533  }
29534  $x1 = (isset($attribs['x1'])?$this->getHTMLUnitToUnits($attribs['x1'], 0, $this->svgunit, false):0);
29535  $y1 = (isset($attribs['y1'])?$this->getHTMLUnitToUnits($attribs['y1'], 0, $this->svgunit, false):0);
29536  $x2 = (isset($attribs['x2'])?$this->getHTMLUnitToUnits($attribs['x2'], 0, $this->svgunit, false):0);
29537  $y2 = (isset($attribs['y2'])?$this->getHTMLUnitToUnits($attribs['y2'], 0, $this->svgunit, false):0);
29538  $x = $x1;
29539  $y = $y1;
29540  $w = abs($x2 - $x1);
29541  $h = abs($y2 - $y1);
29542  if (!$clipping) {
29543  $this->StartTransform();
29544  $this->SVGTransform($tm);
29545  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Line', array($x1, $y1, $x2, $y2));
29546  $this->Line($x1, $y1, $x2, $y2);
29547  $this->StopTransform();
29548  }
29549  break;
29550  }
29551  case 'polyline':
29552  case 'polygon': {
29553  if ($invisible) {
29554  break;
29555  }
29556  $points = (isset($attribs['points'])?$attribs['points']:'0 0');
29557  $points = trim($points);
29558  // note that point may use a complex syntax not covered here
29559  $points = preg_split('/[\,\s]+/si', $points);
29560  if (count($points) < 4) {
29561  break;
29562  }
29563  $p = array();
29564  $xmin = 2147483647;
29565  $xmax = 0;
29566  $ymin = 2147483647;
29567  $ymax = 0;
29568  foreach ($points as $key => $val) {
29569  $p[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false);
29570  if (($key % 2) == 0) {
29571  // X coordinate
29572  $xmin = min($xmin, $p[$key]);
29573  $xmax = max($xmax, $p[$key]);
29574  } else {
29575  // Y coordinate
29576  $ymin = min($ymin, $p[$key]);
29577  $ymax = max($ymax, $p[$key]);
29578  }
29579  }
29580  $x = $xmin;
29581  $y = $ymin;
29582  $w = ($xmax - $xmin);
29583  $h = ($ymax - $ymin);
29584  if ($name == 'polyline') {
29585  $this->StartTransform();
29586  $this->SVGTransform($tm);
29587  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'PolyLine', array($p, 'CNZ'));
29588  if (!empty($obstyle)) {
29589  $this->PolyLine($p, $obstyle, array(), array());
29590  }
29591  $this->StopTransform();
29592  } else { // polygon
29593  if ($clipping) {
29594  $this->SVGTransform($tm);
29595  $this->Polygon($p, 'CNZ', array(), array(), true);
29596  } else {
29597  $this->StartTransform();
29598  $this->SVGTransform($tm);
29599  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Polygon', array($p, 'CNZ'));
29600  if (!empty($obstyle)) {
29601  $this->Polygon($p, $obstyle, array(), array(), true);
29602  }
29603  $this->StopTransform();
29604  }
29605  }
29606  break;
29607  }
29608  // image
29609  case 'image': {
29610  if ($invisible) {
29611  break;
29612  }
29613  if (!isset($attribs['xlink:href']) OR empty($attribs['xlink:href'])) {
29614  break;
29615  }
29616  $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0);
29617  $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0);
29618  $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0);
29619  $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0);
29620  $img = $attribs['xlink:href'];
29621  if (!$clipping) {
29622  $this->StartTransform();
29623  $this->SVGTransform($tm);
29624  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h);
29625  if (preg_match('/^data:image\/[^;]+;base64,/', $img, $m) > 0) {
29626  // embedded image encoded as base64
29627  $img = '@'.base64_decode(substr($img, strlen($m[0])));
29628  } else {
29629  // fix image path
29630  if (!$this->empty_string($this->svgdir) AND (($img{0} == '.') OR (basename($img) == $img))) {
29631  // replace relative path with full server path
29632  $img = $this->svgdir.'/'.$img;
29633  }
29634  if (($img[0] == '/') AND !empty($_SERVER['DOCUMENT_ROOT']) AND ($_SERVER['DOCUMENT_ROOT'] != '/')) {
29635  $findroot = strpos($img, $_SERVER['DOCUMENT_ROOT']);
29636  if (($findroot === false) OR ($findroot > 1)) {
29637  if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') {
29638  $img = substr($_SERVER['DOCUMENT_ROOT'], 0, -1).$img;
29639  } else {
29640  $img = $_SERVER['DOCUMENT_ROOT'].$img;
29641  }
29642  }
29643  }
29644  $img = urldecode($img);
29645  $testscrtype = @parse_url($img);
29646  if (!isset($testscrtype['query']) OR empty($testscrtype['query'])) {
29647  // convert URL to server path
29648  $img = str_replace(K_PATH_URL, K_PATH_MAIN, $img);
29649  }
29650  }
29651  // get image type
29652  $imgtype = $this->getImageFileType($img);
29653  if (($imgtype == 'eps') OR ($imgtype == 'ai')) {
29654  $this->ImageEps($img, $x, $y, $w, $h);
29655  } elseif ($imgtype == 'svg') {
29656  $this->ImageSVG($img, $x, $y, $w, $h);
29657  } else {
29658  $this->Image($img, $x, $y, $w, $h);
29659  }
29660  $this->StopTransform();
29661  }
29662  break;
29663  }
29664  // text
29665  case 'text':
29666  case 'tspan': {
29667  // only basic support - advanced features must be implemented
29668  $this->svgtextmode['invisible'] = $invisible;
29669  if ($invisible) {
29670  break;
29671  }
29672  array_push($this->svgstyles, $svgstyle);
29673  if (isset($attribs['x'])) {
29674  $x = $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false);
29675  } elseif ($name == 'tspan') {
29676  $x = $this->x;
29677  } else {
29678  $x = 0;
29679  }
29680  if (isset($attribs['y'])) {
29681  $y = $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false);
29682  } elseif ($name == 'tspan') {
29683  $y = $this->y;
29684  } else {
29685  $y = 0;
29686  }
29687  $svgstyle['text-color'] = $svgstyle['fill'];
29688  $this->svgtext = '';
29689  if (isset($svgstyle['text-anchor'])) {
29690  $this->svgtextmode['text-anchor'] = $svgstyle['text-anchor'];
29691  } else {
29692  $this->svgtextmode['text-anchor'] = 'start';
29693  }
29694  if (isset($svgstyle['direction'])) {
29695  if ($svgstyle['direction'] == 'rtl') {
29696  $this->svgtextmode['rtl'] = true;
29697  } else {
29698  $this->svgtextmode['rtl'] = false;
29699  }
29700  } else {
29701  $this->svgtextmode['rtl'] = false;
29702  }
29703  if (isset($svgstyle['stroke']) AND ($svgstyle['stroke'] != 'none') AND isset($svgstyle['stroke-width']) AND ($svgstyle['stroke-width'] > 0)) {
29704  $this->svgtextmode['stroke'] = $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false);
29705  } else {
29706  $this->svgtextmode['stroke'] = false;
29707  }
29708  $this->StartTransform();
29709  $this->SVGTransform($tm);
29710  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, 1, 1);
29711  $this->x = $x;
29712  $this->y = $y;
29713  break;
29714  }
29715  // use
29716  case 'use': {
29717  if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
29718  $svgdefid = substr($attribs['xlink:href'], 1);
29719  if (isset($this->svgdefs[$svgdefid])) {
29720  $use = $this->svgdefs[$svgdefid];
29721  if (isset($attribs['xlink:href'])) {
29722  unset($attribs['xlink:href']);
29723  }
29724  if (isset($attribs['id'])) {
29725  unset($attribs['id']);
29726  }
29727  $attribs = array_merge($attribs, $use['attribs']);
29728  $this->startSVGElementHandler($parser, $use['name'], $attribs);
29729  }
29730  }
29731  break;
29732  }
29733  default: {
29734  break;
29735  }
29736  } // end of switch
29737  }
29738 
29747  protected function endSVGElementHandler($parser, $name) {
29748  switch($name) {
29749  case 'defs': {
29750  $this->svgdefsmode = false;
29751  break;
29752  }
29753  // clipPath
29754  case 'clipPath': {
29755  $this->svgclipmode = false;
29756  break;
29757  }
29758  case 'g': {
29759  // ungroup: remove last style from array
29760  array_pop($this->svgstyles);
29761  $this->StopTransform();
29762  break;
29763  }
29764  case 'text':
29765  case 'tspan': {
29766  if ($this->svgtextmode['invisible']) {
29767  // This implementation must be fixed to following the rule:
29768  // 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.
29769  break;
29770  }
29771  // print text
29772  $text = $this->svgtext;
29773  //$text = $this->stringTrim($text);
29774  $textlen = $this->GetStringWidth($text);
29775  if ($this->svgtextmode['text-anchor'] != 'start') {
29776  // check if string is RTL text
29777  if ($this->svgtextmode['text-anchor'] == 'end') {
29778  if ($this->svgtextmode['rtl']) {
29779  $this->x += $textlen;
29780  } else {
29781  $this->x -= $textlen;
29782  }
29783  } elseif ($this->svgtextmode['text-anchor'] == 'middle') {
29784  if ($this->svgtextmode['rtl']) {
29785  $this->x += ($textlen / 2);
29786  } else {
29787  $this->x -= ($textlen / 2);
29788  }
29789  }
29790  }
29791  $textrendermode = $this->textrendermode;
29792  $textstrokewidth = $this->textstrokewidth;
29793  $this->setTextRenderingMode($this->svgtextmode['stroke'], true, false);
29794  if ($name == 'text') {
29795  // store current coordinates
29796  $tmpx = $this->x;
29797  $tmpy = $this->y;
29798  }
29799  $this->Cell($textlen, 0, $text, 0, 0, '', false, '', 0, false, 'L', 'T');
29800  if ($name == 'text') {
29801  // restore coordinates
29802  $this->x = $tmpx;
29803  $this->y = $tmpy;
29804  }
29805  // restore previous rendering mode
29806  $this->textrendermode = $textrendermode;
29807  $this->textstrokewidth = $textstrokewidth;
29808  $this->svgtext = '';
29809  $this->StopTransform();
29810  array_pop($this->svgstyles);
29811  break;
29812  }
29813  default: {
29814  break;
29815  }
29816  }
29817  }
29818 
29827  protected function segSVGContentHandler($parser, $data) {
29828  $this->svgtext .= $data;
29829  }
29830 
29831  // --- END SVG METHODS -----------------------------------------------------
29832 
29833 } // END OF TCPDF CLASS
29834 
29835 //============================================================+
29836 // END OF FILE
29837 //============================================================+
getMargins()
Returns an array containing current margins:
Definition: tcpdf.php:20635
Transform($tm)
Apply graphic transformations.
Definition: tcpdf.php:15246
getPageDimensions($pagenum='')
Returns an array of page dimensions:
Definition: tcpdf.php:3338
SetLineWidth($width)
Defines the line width.
Definition: tcpdf.php:15278
_putimages()
Output images.
Definition: tcpdf.php:12374
_putshaders()
Output gradient shaders.
Definition: tcpdf.php:19418
$transfmatrix_key
Current key for transformation matrix.
Definition: tcpdf.php:1161
SetDrawSpotColor($name, $tint=100)
Defines the spot color used for all drawing operations (lines, rectangles and cell borders)...
Definition: tcpdf.php:4690
setSpacesRE($re='/[^\S\xa0]/')
Set regular expression to detect withespaces or word separators.
Definition: tcpdf.php:3190
setFooterData($tc=array(0, 0, 0), $lc=array(0, 0, 0))
Set footer data.
Definition: tcpdf.php:4183
_addfield($type, $name, $x, $y, $w, $h, $prop)
Adds a javascript form field.
Definition: tcpdf.php:17167
setFontStretching($perc=100)
Set the percentage of character stretching.
Definition: tcpdf.php:27887
_outCurve($x1, $y1, $x2, $y2, $x3, $y3)
Append a cubic Bézier curve to the current path.
Definition: tcpdf.php:15431
foreach(array('date1', 'date2', 'type', 'renewals', 'width') as $item) $data
Definition: chart-data.php:29
AddSpotColor($name, $c, $m, $y, $k)
Defines a new spot color.
Definition: tcpdf.php:4608
addHtmlLink($url, $name, $fill=false, $firstline=false, $color='', $style=-1, $firstblock=false)
Output anchor link.
Definition: tcpdf.php:14007
setJPEGQuality($quality)
Set the default JPEG compression quality (1-100)
Definition: tcpdf.php:18876
getCSSBorderDashStyle($style)
Returns the border dash style from CSS property.
Definition: tcpdf.php:21135
$rasterize_vector_images
Boolean flag: if true convert vector images (SVG, EPS) to raster image using GD or ImageMagick librar...
Definition: tcpdf.php:1609
copyPage($page=0)
Clone the specified page to a new page.
Definition: tcpdf.php:26485
setFooter()
This method is used to render the page footer.
Definition: tcpdf.php:4467
_parsejpeg($file)
Extract info from a JPEG file without using the GD library.
Definition: tcpdf.php:8429
PolyLine($p, $style='', $line_style=array(), $fill_color=array())
Draws a polygonal line.
Definition: tcpdf.php:15825
empty_string($str)
Determine whether a string is empty.
Definition: tcpdf.php:26971
$default_graphic_vars
Array of default graphic settings.
Definition: tcpdf.php:1623
$default_form_prop
Deafult Javascript field properties.
Definition: tcpdf.php:1449
const K_BLANK_IMAGE
blank image
formatTOCPageNumber($num)
Format the page numbers on the Table Of Content.
Definition: tcpdf.php:18606
getSpotColor($name)
Return the Spot color array.
Definition: tcpdf.php:4622
_getSHORT($str, $offset)
Get SHORT from string (Big Endian 16-bit signed integer).
Definition: tcpdf.php:10247
getLastH()
Get the last cell height.
Definition: tcpdf.php:3303
_freadint($f)
Read a 4-byte (32 bit) integer from file.
Definition: tcpdf.php:13414
$font_size
Definition: chart-data.php:34
$javascript
Javascript code.
Definition: tcpdf.php:937
$crMargin
Cell right margin (used by regions).
Definition: tcpdf.php:293
getGroupPageNo()
Return the current page in the group.
Definition: tcpdf.php:18573
serializeTCPDFtagParameters($pararray)
Serialize an array of parameters to be used with TCPDF tag in HTML code.
Definition: tcpdf.php:27160
Arrow($x0, $y0, $x1, $y1, $head_style=0, $arm_size=5, $arm_angle=15)
Draws a grahic arrow.
Definition: tcpdf.php:16124
$booklet
Booklet mode for double-sided pages.
Definition: tcpdf.php:1168
_parsepng($file)
Extract info from a PNG file without using the GD library.
Definition: tcpdf.php:8502
$AutoPageBreak
Automatic page breaking.
Definition: tcpdf.php:477
$last_enc_key_c
Last RC4 computed key.
Definition: tcpdf.php:899
$header_string
String to pring on page header after title.
Definition: tcpdf.php:732
SetDrawColorArray($color, $ret=false)
Defines the color used for all drawing operations (lines, rectangles and cell borders).
Definition: tcpdf.php:4760
getPageHeight($pagenum='')
Returns the page height in units.
Definition: tcpdf.php:3370
getPageGroupAlias()
Return the alias for the total number of pages in the current page group.
Definition: tcpdf.php:18545
SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone')
Defines the way the document is to be displayed by the viewer.
Definition: tcpdf.php:3667
$listnum
HTML PARSER: current list nesting level.
Definition: tcpdf.php:811
_putencryption()
Put encryption on PDF document.
Definition: tcpdf.php:14315
$fhPt
Height of page format in points.
Definition: tcpdf.php:245
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:28107
$underline
Underlining flag.
Definition: tcpdf.php:423
SetMargins($left, $top, $right=-1, $keepmargins=false)
Defines the left, top and right margins.
Definition: tcpdf.php:3414
$FontSize
Current font size in user unit.
Definition: tcpdf.php:447
getHtmlDomArray($html)
Returns the HTML DOM array.
Definition: tcpdf.php:21465
$imgscale
Adjusting factor to convert pixels to user units.
Definition: tcpdf.php:603
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:7273
convertHTMLColorToDec($hcolor='#FFFFFF', $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.
Definition: tcpdf.php:14052
const K_SMALL_RATIO
reduction factor for small font
$subject
Document subject.
Definition: tcpdf.php:525
setImageScale($scale)
Set the adjusting factor to convert pixels to user units.
Definition: tcpdf.php:3314
Link($x, $y, $w, $h, $link, $spaces=0)
Puts a link on a rectangular area of the page.
Definition: tcpdf.php:5669
const K_PATH_URL(isset($_SERVER['HTTP_HOST']) AND(!empty($_SERVER['HTTP_HOST'])))
URL path to tcpdf installation folder (http://localhost/tcpdf/).
$linestyleJoin
PDF string for join value of the last line.
Definition: tcpdf.php:1105
$encoding
Default encoding.
Definition: tcpdf.php:848
AddLink()
Creates a new internal link and returns its identifier.
Definition: tcpdf.php:5630
write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres='', $style='', $align='')
Print a Linear Barcode.
Definition: tcpdf.php:20038
_escapetext($s)
THIS METHOD IS DEPRECATED Format a text string.
Definition: tcpdf.php:13555
getInternalPageNumberAliases($a= '')
Return an array containing variations for the basic page number alias.
Definition: tcpdf.php:9236
$svgclipid
ID of last SVG clipPath.
Definition: tcpdf.php:1777
_enddoc()
Output end of document (EOF).
Definition: tcpdf.php:13158
Close()
Terminates the PDF document.
Definition: tcpdf.php:3862
writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='')
Allows to preserve some HTML formatting (limited support).
Definition: tcpdf.php:22293
$error_message
Definition: choixbase.php:18
$colors
Definition: chart-data.php:32
Polygon($p, $style='', $line_style=array(), $fill_color=array(), $closed=true)
Draws a polygon.
Definition: tcpdf.php:15844
$linestyleWidth
PDF string for width value of the last line.
Definition: tcpdf.php:1091
$pagelen
Array containing page lengths in bytes.
Definition: tcpdf.php:1246
SetLineStyle($style, $ret=false)
Set line style.
Definition: tcpdf.php:15321
resetHeaderTemplate()
Reset the xobject template used by Header() method.
Definition: tcpdf.php:4285
setSignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1)
Set the digital signature appearance (a cliccable rectangle area to get signature properties) ...
Definition: tcpdf.php:18398
_putextgstates()
Put extgstates for object transparency.
Definition: tcpdf.php:18766
_getFIXED($str, $offset)
Get FIXED from string (32-bit signed fixed-point number (16.16).
Definition: tcpdf.php:10292
addExtGState($parms)
Add transparency parameters to the current extgstate.
Definition: tcpdf.php:18722
$customlistindent
HTML PARSER: custom indent amount for lists.
Definition: tcpdf.php:1189
_dooverlinew($x, $y, $w)
Overline for rectangular text area.
Definition: tcpdf.php:13402
_dounderlinew($x, $y, $w)
Underline for rectangular text area.
Definition: tcpdf.php:13351
$lasth
Height of last cell printed.
Definition: tcpdf.php:337
$radio_groups
List of radio group objects IDs.
Definition: tcpdf.php:1498
_putEmbeddedFiles()
Embedd the attached files.
Definition: tcpdf.php:5776
AcceptPageBreak()
Whenever a page break condition is met, the method is called, and the break is issued or not dependin...
Definition: tcpdf.php:5841
$colxshift
Array of: X difference between table cell x start and starting page margin, cellspacing, cellpadding.
Definition: tcpdf.php:1568
_UEvalue()
Compute UE value (used for encryption)
Definition: tcpdf.php:14546
$htmlLinkFontStyle
Default font style to add to html links.
Definition: tcpdf.php:1232
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:4991
_RC4($key, $text)
Returns the input text encrypted using RC4 algorithm and the specified key.
Definition: tcpdf.php:14438
Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array())
Draws a Bezier curve.
Definition: tcpdf.php:15568
_dochecks()
Check for locale-related bug.
Definition: tcpdf.php:9207
getPageBuffer($page)
Get page buffer content.
Definition: tcpdf.php:26001
$pagegroups
Array that contains the number of pages in each page group.
Definition: tcpdf.php:979
endLayer()
End the current PDF layer.
Definition: tcpdf.php:18670
_putxobjects()
Output Form XObjects Templates.
Definition: tcpdf.php:12483
setVisibility($v)
Set the visibility of the successive elements.
Definition: tcpdf.php:18689
_putdests()
Insert Named Destinations.
Definition: tcpdf.php:16861
_fixAES256Password($password)
Convert password for AES-256 encryption mode.
Definition: tcpdf.php:14611
$header_font
Default font used on page header.
Definition: tcpdf.php:678
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:14785
SVGTransform($tm)
Apply SVG graphic transformation matrix.
Definition: tcpdf.php:28546
$isunicode
Boolean flag set to true when the input text is unicode (require unicode fonts).
Definition: tcpdf.php:611
$last_enc_key
Last RC4 key encrypted (cached for optimisation).
Definition: tcpdf.php:892
getPathPaintOperator($style, $default='S')
Get the Path-Painting Operators.
Definition: tcpdf.php:27467
$js_objects
Javascript objects array.
Definition: tcpdf.php:1456
$cell_margin
Array of cell margins ('T' => top, 'R' => right, 'B' => bottom, 'L' => left).
Definition: tcpdf.php:319
getFontStyle()
Returns the current font style.
Definition: tcpdf.php:20706
$pageopen
Store the fage status (true when opened, false when closed).
Definition: tcpdf.php:1302
_outCurveY($x1, $y1, $x3, $y3)
Append a cubic Bézier curve to the current path.
Definition: tcpdf.php:15463
_puttruetypeunicode($font)
Adds unicode fonts.
Definition: tcpdf.php:11917
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:7130
replaceMissingChars($text, $font='', $style='', $subs=array())
Replace missing font characters on selected font with specified substitutions.
Definition: tcpdf.php:5582
$HREF
HTML PARSER: array to store current link and rendering styles.
Definition: tcpdf.php:781
if($rub=fetch_assoc($rub_query)) if(check_if_module_active('url_rewriting')) $class
Definition: index.php:68
$header_text_color
Color for header text (RGB array).
Definition: tcpdf.php:739
fitBlock($w, $h, $x, $y, $fitonpage=false)
Set the block dimensions accounting for page breaks and page/column fitting.
Definition: tcpdf.php:7826
getDocCreationTimestamp()
Returns document creation timestamp in seconds.
Definition: tcpdf.php:13477
PageNoFormatted()
Returns the current page number formatted as a string.
Definition: tcpdf.php:18616
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:4804
_setGDImageTransparency($new_image, $image)
Set the transparency for the given GD image.
Definition: tcpdf.php:8408
getCSSFontSpacing($spacing, $parent=0)
Returns the letter-spacing value from CSS value.
Definition: tcpdf.php:21364
$FontFamily
Current font family.
Definition: tcpdf.php:397
setLIsymbol($symbol='!')
Set the default bullet to be used as LI bullet symbol.
Definition: tcpdf.php:25268
removeSHY($txt='')
Removes SHY characters from text.
Definition: tcpdf.php:5919
setRasterizeVectorImages($mode)
Enable/disable rasterization of vector images using ImageMagick library.
Definition: tcpdf.php:27442
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:4925
SetDefaultMonospacedFont($font)
Defines the default monospaced font.
Definition: tcpdf.php:5619
$tagvspaces
Array used for custom vertical spaces for HTML tags.
Definition: tcpdf.php:1182
_getBYTE($str, $offset)
Get BYTE from string (8-bit unsigned integer).
Definition: tcpdf.php:10310
$print_header
Boolean flag to print/hide page header.
Definition: tcpdf.php:702
$signature_data
Digital signature data.
Definition: tcpdf.php:1372
Output($name='doc.pdf', $dest='I')
Send the document to a given destination: string, local file or browser.
Definition: tcpdf.php:8958
getCharBBox($char)
Returns the glyph bounding box of the specified character in the current font in user units...
Definition: tcpdf.php:5497
SetAbsXY($x, $y)
Set the absolute X and Y coordinates of the current pointer.
Definition: tcpdf.php:8928
$form_mode
Current method to submit forms.
Definition: tcpdf.php:1477
getCellMargins()
Get the internal Cell margin array.
Definition: tcpdf.php:3557
movePage($frompage, $topage)
Move a page to a previous position.
Definition: tcpdf.php:26142
$bufferlen
Length of the buffer in bytes.
Definition: tcpdf.php:1267
SetY($y, $resetx=true, $rtloff=false)
Moves the current abscissa back to the left margin and sets the ordinate.
Definition: tcpdf.php:8860
_endpage()
Mark end of page.
Definition: tcpdf.php:13299
$pdflayers
Array of PDF layers data.
Definition: tcpdf.php:1693
setTempRTL($mode)
Force temporary RTL language direction.
Definition: tcpdf.php:3241
getPageRegions()
Return an array of no-write page regions.
Definition: tcpdf.php:27932
setPDFVersion($version='1.7')
Set the PDF version (check PDF reference for valid values).
Definition: tcpdf.php:18918
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:25398
encodeNameObject($name)
Encode a name object.
Definition: tcpdf.php:16788
$pagedim
Page dimensions.
Definition: tcpdf.php:227
_getstream($s, $n=0)
Format output stream (DEPRECATED).
Definition: tcpdf.php:13603
Rotate($angle, $x='', $y='')
Rotate object.
Definition: tcpdf.php:15158
$pages
Array containing pages.
Definition: tcpdf.php:203
$CurOrientation
Current page orientation (P = Portrait, L = Landscape).
Definition: tcpdf.php:221
PieSectorXY($xc, $yc, $rx, $ry, $a, $b, $style='FD', $cw=false, $o=0, $nc=2)
Draw the sector of an ellipse.
Definition: tcpdf.php:19631
setHtmlVSpace($tagvs)
Set the vertical spaces for HTML tags.
Definition: tcpdf.php:25352
getFontFamily()
Returns the current font family name.
Definition: tcpdf.php:20696
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:26767
GetX()
Returns the relative X value of current position.
Definition: tcpdf.php:8787
getFontFamilyName($fontfamily)
Return normalized font name.
Definition: tcpdf.php:27623
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:19194
setRTL($enable, $resetx=true)
Enable or disable Right-To-Left language mode.
Definition: tcpdf.php:3215
$original_lMargin
Original left margin value.
Definition: tcpdf.php:665
$spotcolor
Array containing spot color names and values.
Definition: tcpdf.php:1686
$tempfontsize
Temporary font size in points.
Definition: tcpdf.php:835
_toPNG($image)
Convert the loaded image to a PNG and then return a structure for the PDF creator.
Definition: tcpdf.php:8384
$numfonts
Counts the number of fonts.
Definition: tcpdf.php:1281
replaceBuffer($data)
Replace the buffer content.
Definition: tcpdf.php:25940
$svggradients
Array of SVG gradients.
Definition: tcpdf.php:1728
$spot_colors
Array of Spot colors.
Definition: tcpdf.php:1133
hyphenateText($text, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8)
Returns text with soft hyphens.
Definition: tcpdf.php:27396
$webcolor
Array containing HTML color names and values.
Definition: tcpdf.php:1679
_outLine($x, $y)
Append a straight line segment from the current point to the point (x, y).
Definition: tcpdf.php:15397
$n
Current object number.
Definition: tcpdf.php:179
LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0, 0, 1, 0))
Paints a linear colour gradient.
Definition: tcpdf.php:19153
$bMargin
Page break margin.
Definition: tcpdf.php:305
$fwPt
Width of page format in points.
Definition: tcpdf.php:239
$outlines
Outlines for bookmark.
Definition: tcpdf.php:921
setCellHeightRatio($h)
Set the height of the cell (line height) respect the font height.
Definition: tcpdf.php:18899
$check_page_regions
Boolean value true when page region check is active.
Definition: tcpdf.php:1672
setHeaderTemplateAutoreset($val=true)
Set a flag to automatically reset the xobject template used by Header() method at each page...
Definition: tcpdf.php:4294
getCellPaddings()
Get the internal Cell padding array.
Definition: tcpdf.php:3521
$k
Scale factor (number of points in user unit).
Definition: tcpdf.php:233
RadioButton($name, $w, $prop=array(), $opt=array(), $onvalue='On', $checked=false, $x='', $y='', $js=false)
Creates a RadioButton field.
Definition: tcpdf.php:17718
$epsmarker
String used to mark the beginning and end of EPS image blocks.
Definition: tcpdf.php:1147
$PageAnnots
Array of Annotations in pages.
Definition: tcpdf.php:385
$w
Current width of page in user unit.
Definition: tcpdf.php:263
getRawCharWidth($char)
Returns the length of the char in user unit for the current font.
Definition: tcpdf.php:5050
$objcopy
Cloned copy of the current class object.
Definition: tcpdf.php:1316
addTOCPage($orientation='', $format='', $keepmargins=false)
Adds a new TOC (Table Of Content) page to the document.
Definition: tcpdf.php:3988
sortBookmarks()
Sort bookmarks for page and key.
Definition: tcpdf.php:16943
$header_logo_width
Width of header image logo in user units.
Definition: tcpdf.php:720
$default_table_columns
Default number of columns for html table.
Definition: tcpdf.php:773
_datestring($n=0, $timestamp=0)
Returns a formatted date for meta information.
Definition: tcpdf.php:13525
_getTTFtableChecksum($table, $length)
Returs the checksum of a TTF table.
Definition: tcpdf.php:11632
$svgtext
SVG text.
Definition: tcpdf.php:1784
getColorStringFromArray($c)
Convert a color array into a string representation.
Definition: tcpdf.php:4888
_putsignature()
Add certification signature (DocMDP or UR3) You can set only one signature type.
Definition: tcpdf.php:18240
MirrorL($angle=0, $x='', $y='')
Reflection against a straight line through point (x, y) with the gradient angle (angle).
Definition: tcpdf.php:15101
setGraphicVars($gvars, $extended=false)
Set graphic variables.
Definition: tcpdf.php:25805
startLayer($name='', $print=true, $view=true)
Start a new pdf layer.
Definition: tcpdf.php:18650
SetCreator($creator)
Defines the creator of the document.
Definition: tcpdf.php:3824
$diffs
Array of encoding differences.
Definition: tcpdf.php:367
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:27227
getAutoPageBreak()
Return the auto-page-break mode (true or false).
Definition: tcpdf.php:3655
getBuffer()
Get buffer content.
Definition: tcpdf.php:25958
$alias_tot_pages
String alias for total number of pages.
Definition: tcpdf.php:555
$buffer
Buffer holding in-memory PDF.
Definition: tcpdf.php:197
addJavascriptObject($script, $onload=false)
Adds a javascript object and return object ID.
Definition: tcpdf.php:17073
$column_start_page
Starting page for columns.
Definition: tcpdf.php:1554
arrUTF8ToUTF16BE($unicode, $setbom=false)
Converts array of UTF-8 characters to UTF16-BE string.
Definition: tcpdf.php:13902
$file_id
File ID (used on document trailer).
Definition: tcpdf.php:912
Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array())
Draws a rectangle.
Definition: tcpdf.php:15510
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:7236
$keywords
Document keywords.
Definition: tcpdf.php:537
SetX($x, $rtloff=false)
Defines the abscissa of the current position.
Definition: tcpdf.php:8827
$cache_file_length
Array used to store the lengths of cache files.
Definition: tcpdf.php:1323
$LineWidth
Line width in user unit.
Definition: tcpdf.php:343
StartTransform()
Starts a 2D tranformation saving current graphic state.
Definition: tcpdf.php:14944
$PageBreakTrigger
Threshold used to trigger page breaks.
Definition: tcpdf.php:483
$FontAscent
Current font ascent (distance between font top and baseline).
Definition: tcpdf.php:410
$pageobjects
Array of object IDs for each page.
Definition: tcpdf.php:191
GetNumChars($s)
Returns the numbero of characters in a string.
Definition: tcpdf.php:5076
$thead
Table header content to be repeated on each new page.
Definition: tcpdf.php:1330
$CurrentFont
Current font info.
Definition: tcpdf.php:435
_putbookmarks()
Create a bookmark PDF string.
Definition: tcpdf.php:16961
getAbsFontMeasure($s)
Convert a relative font measure into absolute value.
Definition: tcpdf.php:5487
$pdfproducer
Set the document producer metadata.
Definition: tcpdf.php:1934
SetTitle($title)
Defines the title of the document.
Definition: tcpdf.php:3780
SetTopMargin($margin)
Defines the top margin.
Definition: tcpdf.php:3451
$numpages
Counts the number of pages.
Definition: tcpdf.php:1239
getFontSize()
Returns the current font size.
Definition: tcpdf.php:20676
$mode
getAliasRightShift()
Returns the string alias used right align page numbers.
Definition: tcpdf.php:18494
getRandomSeed($seed='')
Returns a string containing random data to be used as a seed for encryption methods.
Definition: tcpdf.php:14219
getVectorsAngle($x1, $y1, $x2, $y2)
Returns the angle in radiants between two vectors.
Definition: tcpdf.php:29211
checkPageRegions($h, $x, $y)
Check page for no-write regions and adapt current coordinates and page margins if necessary...
Definition: tcpdf.php:28004
_putcidfont0($font)
Output CID-0 fonts.
Definition: tcpdf.php:12304
const K_PATH_IMAGES
images directory
_AES($key, $text)
Returns the input text exrypted using AES algorithm and the specified key.
Definition: tcpdf.php:14484
startPage($orientation='', $format='', $tocpage=false)
Starts a new page to the document.
Definition: tcpdf.php:4063
setImageBuffer($image, $data)
Set image buffer content.
Definition: tcpdf.php:26018
_md5_16($str)
Encrypts a string using MD5 and returns it's value as a binary string.
Definition: tcpdf.php:14502
$offsets
Array of object offsets.
Definition: tcpdf.php:185
MirrorH($x='')
Horizontal Mirroring.
Definition: tcpdf.php:15065
Circle($x0, $y0, $r, $angstr=0, $angend=360, $style='', $line_style=array(), $fill_color=array(), $nc=2)
Draws a circle.
Definition: tcpdf.php:15807
convertSVGtMatrix($tm)
Convert SVG transformation matrix to PDF.
Definition: tcpdf.php:28526
SetLeftMargin($margin)
Defines the left margin.
Definition: tcpdf.php:3436
setBookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0, 0, 0))
Adds a bookmark - alias for Bookmark().
Definition: tcpdf.php:16885
$cell_padding
Array of cell internal paddings ('T' => top, 'R' => right, 'B' => bottom, 'L' => left).
Definition: tcpdf.php:312
objclone($object)
Creates a copy of a class object.
Definition: tcpdf.php:26960
ScaleXY($s, $x='', $y='')
Vertical and horizontal proportional Scaling.
Definition: tcpdf.php:15018
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:28565
$bordermrk
Array used to store positions inside the pages buffer (keys are the page numbers).
Definition: tcpdf.php:1042
stringRightTrim($str, $replace='')
Right trim the input string.
Definition: tcpdf.php:27585
$signature_appearance
Data for digital signature appearance.
Definition: tcpdf.php:1386
AddFont($family, $style='', $fontfile='', $subset='default')
Imports a TrueType, Type1, core, or CID0 font and makes it available.
Definition: tcpdf.php:5111
_putocg()
Put pdf layers.
Definition: tcpdf.php:18625
$form_obj_id
List of form annotations IDs.
Definition: tcpdf.php:1442
$alias_right_shift
String alias for right shift compensation used to correctly align page numbers on the right...
Definition: tcpdf.php:579
const K_CELL_HEIGHT_RATIO
height of cell respect font height
$bgcolor
Current background color.
Definition: tcpdf.php:829
$alias_group_tot_pages
String alias for total number of pages in a single group.
Definition: tcpdf.php:567
$tocpage
Boolean flag true when we are on TOC (Table Of Content) page.
Definition: tcpdf.php:1602
getSpaceString()
Returns the string used to find spaces.
Definition: tcpdf.php:22250
$img_rb_x
The right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image.
Definition: tcpdf.php:587
PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90)
Draw the sector of a circle.
Definition: tcpdf.php:19610
setStartingPageNumber($num=1)
Set the starting page number.
Definition: tcpdf.php:18483
$svgstyles
Array of SVG properties.
Definition: tcpdf.php:1805
const K_PATH_CACHE
cache directory for temporary files (full path)
$encmaps
Object containing font encoding maps.
Definition: tcpdf.php:627
lastPage($resetmargins=false)
Reset pointer to the last document page.
Definition: tcpdf.php:3953
_escape($s)
Add "\" before "\", "(" and ")".
Definition: tcpdf.php:13425
getScaleFactor()
Returns the scale factor (number of points in user unit).
Definition: tcpdf.php:3400
setPageFormat($format, $orientation='P')
Change the format of the current page.
Definition: tcpdf.php:2918
setPageBuffer($page, $data, $append=false)
Set page buffer content.
Definition: tcpdf.php:25974
$start_transaction_page
Store page number when startTransaction() is called.
Definition: tcpdf.php:1512
GetLineWidth()
Returns the current the line width.
Definition: tcpdf.php:15294
$LayoutMode
Layout display mode.
Definition: tcpdf.php:507
setPrintFooter($val=true)
Set a flag to print page footer.
Definition: tcpdf.php:4259
Skew($angle_x, $angle_y, $x='', $y='')
Skew.
Definition: tcpdf.php:15215
$svggradientid
ID of last SVG gradient.
Definition: tcpdf.php:1735
$title
Document title.
Definition: tcpdf.php:519
UTF8ArrToLatin1($unicode)
Converts UTF-8 characters array to array of Latin1 characters
Definition: tcpdf.php:13844
replaceRightShiftPageNumAliases($page, $aliases, $diff)
Replace right shift page number aliases with spaces to correct right alignment.
Definition: tcpdf.php:9295
IncludeJS($script)
Adds a javascript.
Definition: tcpdf.php:17060
_objectkey($n)
Compute encryption key depending on object number where the encrypted data is stored.
Definition: tcpdf.php:14267
addTTFfont($fontfile, $fonttype='', $enc='', $flags=32, $outpath='', $platid=3, $encid=1, $addcbbox=false)
Convert and add the selected TrueType or Type1 font to the fonts folder (that must be writeable)...
Definition: tcpdf.php:10350
getImageRBX()
Return the right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image...
Definition: tcpdf.php:4268
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:19675
convertStringToHexString($s)
Convert string to hexadecimal string (byte string)
Definition: tcpdf.php:14906
$intmrk
Array used to store positions inside the pages buffer (keys are the page numbers).
Definition: tcpdf.php:1035
$start
Definition: attributs.php:22
$page_boxes
Define the page boundaries boxes to be set on document.
Definition: tcpdf.php:1927
$custom_xmp
Custom XMP data.
Definition: tcpdf.php:1904
getTimestamp($date)
Returns timestamp in seconds from formatted date-time.
Definition: tcpdf.php:13498
SetAbsY($y)
Set the absolute Y coordinate of the current pointer.
Definition: tcpdf.php:8916
$newline
Boolean flag to indicate if a new line is created.
Definition: tcpdf.php:1077
$currpagegroup
Current page group number.
Definition: tcpdf.php:986
extractCSSproperties($cssdata)
Extracts the CSS properties from a CSS string.
Definition: tcpdf.php:20787
const PDF_FONT_NAME_MAIN
default main font name
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:4942
$y
Current vertical position in user unit for cell positioning.
Definition: tcpdf.php:331
$header_line_color
Color for header line (RGB array).
Definition: tcpdf.php:746
getTextShadow()
Return the text shadow parameters array.
Definition: tcpdf.php:27269
_destroy($destroyall=false, $preserve_objcopy=false)
Unset all class variables except the following critical variables.
Definition: tcpdf.php:9169
addHTMLVertSpace($hbz=0, $hb=0, $cell=false, $firsttag=false, $lasttag=false)
Add vertical spaces if needed.
Definition: tcpdf.php:25024
$font_obj_ids
Store the font object IDs.
Definition: tcpdf.php:1295
$openMarkedContent
Boolean flag to indicate if marked-content sequence is open.
Definition: tcpdf.php:1119
getRTL()
Return the RTL status.
Definition: tcpdf.php:3231
SetAuthor($author)
Defines the author of the document.
Definition: tcpdf.php:3802
$pdfa_mode
If true set the document to PDF/A mode.
Definition: tcpdf.php:1883
$page_regions
Array of no-write regions.
Definition: tcpdf.php:1666
$radiobutton_groups
List of radio buttons parent objects.
Definition: tcpdf.php:1491
const K_TCPDF_CALLS_IN_HTML
if true allows to call TCPDF methods using HTML syntax IMPORTANT: For security reason, disable this feature if you are printing user HTML content.
rfread($handle, $length)
Binary-safe and URL-safe file read.
Definition: tcpdf.php:8628
_OEvalue()
Compute OE value (used for encryption)
Definition: tcpdf.php:14597
const K_PATH_FONTS
path for PDF fonts use K_PATH_MAIN.
This is a PHP class containing UnicOde data for TCPDF library.
$doc_creation_timestamp
Document creation date-time.
Definition: tcpdf.php:1890
$enc_padding
Encryption padding string.
Definition: tcpdf.php:905
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:15945
RoundedRectXY($x, $y, $w, $h, $rx, $ry, $round_corner='1111', $style='', $border_style=array(), $fill_color=array())
Draws a rounded rectangle.
Definition: tcpdf.php:16054
setEqualColumns($numcols=0, $width=0, $y='')
Set multiple columns of the same size.
Definition: tcpdf.php:27001
_putannotsrefs($n)
Output references to page annotations.
Definition: tcpdf.php:9507
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:5946
set_mqr($mqr)
Sets the current active configuration setting of magic_quotes_runtime (if the set_magic_quotes_runtim...
Definition: tcpdf.php:8333
getCSSMargin($cssmargin, $width=0)
Get the internal Cell margin from CSS attribute.
Definition: tcpdf.php:21276
getDocModificationTimestamp()
Returns document modification timestamp in seconds.
Definition: tcpdf.php:13487
$transfmatrix
Array of transformation matrix.
Definition: tcpdf.php:1154
$fontkeys
Store the font keys.
Definition: tcpdf.php:1288
$gradients
Array for storing gradient information.
Definition: tcpdf.php:1028
getUserPermissionCode($permissions, $mode=0)
Return the premission code used on encryption (P value).
Definition: tcpdf.php:14741
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:5821
setFontBuffer($font, $data)
Set font buffer content.
Definition: tcpdf.php:26079
Clip($x, $y, $w, $h)
Set a rectangular clipping area.
Definition: tcpdf.php:19286
getPage()
Get current document page number.
Definition: tcpdf.php:3964
$img_rb_y
The right-bottom corner Y coordinate of last inserted image.
Definition: tcpdf.php:595
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:5028
setContentMark($page=0)
Set start-writing mark on selected page.
Definition: tcpdf.php:4147
SkewY($angle_y, $x='', $y='')
Skew vertically.
Definition: tcpdf.php:15201
$empty_signature_appearance
Array of empty digital signature appearances.
Definition: tcpdf.php:1393
$dests
A dictionary of names and corresponding destinations (Dests key on document Catalog).
Definition: tcpdf.php:1700
$svgtextmode
SVG text properties.
Definition: tcpdf.php:1791
setFontSubBuffer($font, $key, $data)
Set font buffer content.
Definition: tcpdf.php:26105
const K_PATH_MAIN(substr($k_path_main,-1)!= '/')
Installation path (/var/www/tcpdf/).
$sig_annot_ref
Placemark used during digital signature process.
Definition: tcpdf.php:1428
setBuffer($data)
Set buffer content (always append data).
Definition: tcpdf.php:25922
$imagekeys
Store the image keys.
Definition: tcpdf.php:1260
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:18326
setHeaderData($ln='', $lw=0, $ht='', $hs='', $tc=array(0, 0, 0), $lc=array(0, 0, 0))
Set header data.
Definition: tcpdf.php:4168
stringLeftTrim($str, $replace='')
Left trim the input string.
Definition: tcpdf.php:27572
$strokecolor
Current stroke color.
Definition: tcpdf.php:1589
PHP class to creates array representations for common 1D barcodes to be used with TCPDF (http://www...
Definition: barcodes.php:50
$encryptdata
Array containing encryption settings.
Definition: tcpdf.php:885
setHeaderMargin($hm=10)
Set header margin.
Definition: tcpdf.php:4212
$tcpdflink
If true print TCPDF meta link.
Definition: tcpdf.php:1941
_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:15413
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:15993
setPageBoxes($page, $type, $llx, $lly, $urx, $ury, $points=false)
Set page boundaries.
Definition: tcpdf.php:3036
_getxobjectdict()
Return XObjects Dictionary.
Definition: tcpdf.php:12606
_outCurveV($x2, $y2, $x3, $y3)
Append a cubic Bézier curve to the current path.
Definition: tcpdf.php:15447
$txtshadow
Text shadow data array.
Definition: tcpdf.php:767
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:12756
Footer()
This method is used to render the page footer.
Definition: tcpdf.php:4380
$footer_margin
Minimum distance between footer and bottom page margin.
Definition: tcpdf.php:658
$extgstates
Array of transparency objects and parameters.
Definition: tcpdf.php:993
UTF8ToUTF16BE($str, $setbom=false)
Converts UTF-8 strings to UTF16-BE.
Definition: tcpdf.php:13799
getBarcode()
Get current barcode.
Definition: tcpdf.php:20004
getImageFileType($imgfile, $iminfo=array())
Return the image type given the file name or array returned by getimagesize() function.
Definition: tcpdf.php:7795
_getfontpath()
Return fonts path.
Definition: tcpdf.php:9223
adjustCellPadding($brd=0)
Adjust the internal Cell padding array to take account of the line width.
Definition: tcpdf.php:3568
_putinfo()
Adds some Metadata information (Document Information Dictionary) (see Chapter 14.3.3 Document Information Dictionary of PDF32000_2008.pdf Reference)
Definition: tcpdf.php:12705
$fonts
Array of used fonts.
Definition: tcpdf.php:355
$compress
Compression flag.
Definition: tcpdf.php:215
fixHTMLCode($html, $default_css='', $tagvs='', $tidy_options='')
Cleanup HTML code (requires HTML Tidy library).
Definition: tcpdf.php:20722
SetFont($family, $style='', $size=null, $fontfile='', $subset='default', $out=true)
Sets the font used to print character strings.
Definition: tcpdf.php:5384
TranslateY($t_y)
Translate graphic object vertically.
Definition: tcpdf.php:15124
getEncPermissionsString($protection)
Convert encryption P value to a string of bytes, low-order byte first.
Definition: tcpdf.php:14923
MirrorV($y='')
Verical Mirroring.
Definition: tcpdf.php:15076
stringTrim($str, $replace='')
Trim the input string.
Definition: tcpdf.php:27598
$diskcache
If true enables disk caching.
Definition: tcpdf.php:1274
getFontDescent($font, $style='', $size=0)
Return the font descent value.
Definition: tcpdf.php:5514
deletePage($page)
Remove the specified page.
Definition: tcpdf.php:26305
endSVGElementHandler($parser, $name)
Sets the closing SVG element handler function for the XML parser.
Definition: tcpdf.php:29747
utf8StrRev($str, $setbom=false, $forcertl=false)
Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
Definition: tcpdf.php:16189
$form_action
Current form action (used during XHTML rendering).
Definition: tcpdf.php:1463
$font_stretching
Percentage of character stretching.
Definition: tcpdf.php:1651
setFooterMargin($fm=10)
Set footer margin.
Definition: tcpdf.php:4232
$FontFiles
Array of font files.
Definition: tcpdf.php:361
SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='')
Defines the color used for text.
Definition: tcpdf.php:4959
$svgcliptm
Array of SVG clipPath tranformation matrix.
Definition: tcpdf.php:1770
$listordered
HTML PARSER: array of boolean values, true in case of ordered list (OL), false otherwise.
Definition: tcpdf.php:799
setPrintHeader($val=true)
Set a flag to print page header.
Definition: tcpdf.php:4250
$transfmrk
Array used to store positions of graphics transformation blocks inside the page buffer.
Definition: tcpdf.php:1218
$sign
Boolean flag to enable document digital signature.
Definition: tcpdf.php:1365
setFormDefaultProp($prop=array())
Set default properties for form fields.
Definition: tcpdf.php:17568
getHeaderMargin()
Returns header margin in user units.
Definition: tcpdf.php:4222
getCSSBorderWidth($width)
Returns the border width from CSS property.
Definition: tcpdf.php:21115
$print_footer
Boolean flag to print/hide page footer.
Definition: tcpdf.php:708
getFooterFont()
Get Footer font.
Definition: tcpdf.php:13965
setPage($pnum, $resetmargins=false)
Move pointer at the specified document page and update page dimensions.
Definition: tcpdf.php:3906
utf8Bidi($ta, $str='', $forcertl=false)
Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
Definition: tcpdf.php:16218
setLastH($h)
Set the last cell height.
Definition: tcpdf.php:3284
_putstream($s, $n=0)
Output a stream (DEPRECATED).
Definition: tcpdf.php:13614
_putAPXObject($w=0, $h=0, $stream='')
Put appearance streams XObject used to define annotation's appearance states.
Definition: tcpdf.php:10185
$sig_obj_id
Digital signature object ID.
Definition: tcpdf.php:1414
$FontStyle
Current font style.
Definition: tcpdf.php:403
openHTMLTagHandler($dom, $key, $cell)
Process opening tags.
Definition: tcpdf.php:23819
getHeaderData()
Returns header data:
Definition: tcpdf.php:4195
getCSSPadding($csspadding, $width=0)
Get the internal Cell padding from CSS attribute.
Definition: tcpdf.php:21222
$rMargin
Right margin.
Definition: tcpdf.php:281
$cache_maxsize_UTF8StringToArray
Maximum size of cache array used for UTF8StringToArray() method.
Definition: tcpdf.php:1351
startSVGElementHandler($parser, $name, $attribs, $ctm=array())
Sets the opening SVG element handler function for the XML parser.
Definition: tcpdf.php:29235
$linethrough
line trough state
Definition: tcpdf.php:951
_putcatalog()
Output Catalog.
Definition: tcpdf.php:12904
Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array())
Draws a poly-Bezier curve.
Definition: tcpdf.php:15598
$embeddedfiles
Array of files to embedd.
Definition: tcpdf.php:1203
_putheader()
Output PDF File Header (7.5.2).
Definition: tcpdf.php:13149
if(strlen($date2)== '10') if($type== 'users-by-age'&&a_priv('admin_users', true)) elseif($type== 'forums-count'&&a_priv('admin_content', true)) elseif($type== 'forums-categories'&&a_priv('admin_content', true)) elseif($type== 'users-count'&&a_priv('admin_users', true)) elseif($type== 'product-categories'&&a_priv('admin_products', true)) elseif($type== 'users-by-sex'&&a_priv('admin_users', true)) elseif($type== 'users-by-country'&&a_priv('admin_users', true)) elseif($type== 'sales'&&a_priv('admin_sales', true))
Definition: chart-data.php:160
$ur
Array with additional document-wide usage rights for the document.
Definition: tcpdf.php:958
getFormattedDate($time)
Returns a formatted date-time.
Definition: tcpdf.php:13513
resetLastH()
Reset the last cell height.
Definition: tcpdf.php:3293
UniArrSubString($uniarr, $start='', $end='')
Extract a slice of the $uniarr array and return it as string.
Definition: tcpdf.php:7735
$overprint
Overprint mode array.
Definition: tcpdf.php:1912
$links
Array of internal links.
Definition: tcpdf.php:391
setColumnsArray($columns)
Set columns array.
Definition: tcpdf.php:27046
$default_monospaced_font
Default monospace font.
Definition: tcpdf.php:1309
Button($name, $w, $h, $caption, $action, $prop=array(), $opt=array(), $x='', $y='', $js=false)
Creates a button field.
Definition: tcpdf.php:18073
$textstrokewidth
Text stroke width in doc units.
Definition: tcpdf.php:1582
$header_xobjid
ID of the stored default header template (-1 = not set).
Definition: tcpdf.php:640
getNumPages()
Get the total number of insered pages.
Definition: tcpdf.php:3975
_getUSHORT($str, $offset)
Get USHORT from string (Big Endian 16-bit unsigned integer).
Definition: tcpdf.php:10233
TextField($name, $w, $h, $prop=array(), $opt=array(), $x='', $y='', $js=false)
Creates a text field.
Definition: tcpdf.php:17597
$PageMode
A name object specifying how the document should be displayed when opened.
Definition: tcpdf.php:1021
getNumberOfColumns()
Return the current number of columns.
Definition: tcpdf.php:27149
isValidCSSSelectorForTag($dom, $key, $selector)
Returns true if the CSS selector is valid for the selected HTML tag.
Definition: tcpdf.php:20879
Open()
This method begins the generation of the PDF document.
Definition: tcpdf.php:3850
removePageRegion($key)
Remove a single no-write region.
Definition: tcpdf.php:27986
$creator
Document creator.
Definition: tcpdf.php:543
ScaleY($s_y, $x='', $y='')
Vertical Scaling.
Definition: tcpdf.php:15005
getPageSizeFromFormat($format)
Get page dimensions from format name.
Definition: tcpdf.php:2509
$svgclipmode
Boolean value true when in SVG clipPath tag.
Definition: tcpdf.php:1756
_dounderline($x, $y, $txt)
Underline text.
Definition: tcpdf.php:13338
_putjavascript()
Create a javascript PDF string.
Definition: tcpdf.php:17089
selectColumn($col='')
Set position at a given column.
Definition: tcpdf.php:27060
$annotation_fonts
List of fonts used on form fields (fontname => fontkey).
Definition: tcpdf.php:1484
$endlinex
End position of the latest inserted line.
Definition: tcpdf.php:1084
$footer_font
Default font used on page footer.
Definition: tcpdf.php:684
_dolinethrough($x, $y, $txt)
Line through text.
Definition: tcpdf.php:13363
$svgclippaths
Array of SVG clipPath commands.
Definition: tcpdf.php:1763
setLanguageArray($language)
Set language array.
Definition: tcpdf.php:13975
$cntmrk
Array used to store content positions inside the pages buffer (keys are the page numbers).
Definition: tcpdf.php:1056
$n_js
Javascript counter.
Definition: tcpdf.php:944
$search
Definition: rpc.php:29
startPageGroup($page= '')
Create a new page group.
Definition: tcpdf.php:18452
$h
Current height of page in user unit.
Definition: tcpdf.php:269
updateCIDtoGIDmap($map, $cid, $gid)
Update the CIDToGIDMap string with a new value.
Definition: tcpdf.php:10324
_newobj()
Begin a new object and return the object number.
Definition: tcpdf.php:13309
setPageMark()
Set start-writing mark on current page stream used to put borders and fills.
Definition: tcpdf.php:4134
AliasNumPage($s='')
This method is DEPRECATED and doesn't have any effect.
Definition: tcpdf.php:18475
setOverprint($stroking=true, $nonstroking='', $mode=0)
Set overprint mode for stroking (OP) and non-stroking (op) painting operations.
Definition: tcpdf.php:18795
ListBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false)
Creates a List-box field.
Definition: tcpdf.php:17820
getBorderMode($brd, $position='start')
Get the border mode accounting for multicell position (opens bottom side of multicell crossing pages)...
Definition: tcpdf.php:7052
_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:15679
Ln($h='', $cell=false)
Performs a line break.
Definition: tcpdf.php:8752
if(empty($GLOBALS['site_parameters']['unsubscribe_order_process']))
SetCompression($compress=true)
Activates or deactivates page compression.
Definition: tcpdf.php:3744
write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='', $distort=false)
Print 2D Barcode.
Definition: tcpdf.php:20409
getImageBuffer($image)
Get image buffer content.
Definition: tcpdf.php:26063
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:7915
$footerpos
Array used to store footer positions of each page.
Definition: tcpdf.php:1063
_escapeXML($str)
Escape some special characters (< > &) for XML output.
Definition: tcpdf.php:13574
_generateencryptionkey()
Compute encryption key.
Definition: tcpdf.php:14626
getRemainingWidth()
Returns the remaining width between the current position and margins.
Definition: tcpdf.php:7695
$header_xobj_autoreset
If true reset the Header Xobject template at each page.
Definition: tcpdf.php:646
AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false)
Adds a new page to the document.
Definition: tcpdf.php:4013
$wPt
Current width of page in points.
Definition: tcpdf.php:251
_putfontwidths($font, $cidoffset=0)
Outputs font widths.
Definition: tcpdf.php:11654
setExtGState($gs)
Add an extgstate.
Definition: tcpdf.php:18753
$cell_height_ratio
Default cell height ratio.
Definition: tcpdf.php:1007
$overline
Overlining flag.
Definition: tcpdf.php:429
$premode
Boolean flag to indicate if we are inside a PRE tag.
Definition: tcpdf.php:1210
setColorArray($type, $color, $ret=false)
Set the color array for the specified type ('draw', 'fill', 'text').
Definition: tcpdf.php:4729
$columns
Array of column measures (width, space, starting Y position).
Definition: tcpdf.php:1533
_putannotsobjs()
Output annotations objects for all pages.
Definition: tcpdf.php:9563
PageNo()
Returns the current page number.
Definition: tcpdf.php:4591
const K_THAI_TOPCHARS
set to true to enable the special procedure used to avoid the overlappind of symbols on Thai language...
$dpi
DPI (Dot Per Inch) Document Resolution (do not change).
Definition: tcpdf.php:965
$fontlist
List of available fonts on filesystem.
Definition: tcpdf.php:787
__destruct()
Default destructor.
Definition: tcpdf.php:2133
$rtl
Boolean flag to indicate if the document language is Right-To-Left.
Definition: tcpdf.php:862
$inxobj
Boolean value true when we are inside an XObject.
Definition: tcpdf.php:1637
$l
Language templates.
Definition: tcpdf.php:690
$header_title
Title to be printed on default page header.
Definition: tcpdf.php:726
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:19171
$footerlen
Array used to store footer length of each page.
Definition: tcpdf.php:1070
$newpagegroup
Array of page numbers were a new page group was started (the page numbers are the keys of the array)...
Definition: tcpdf.php:972
inPageBody()
Check if we are on the page body (excluding page header and footer).
Definition: tcpdf.php:4523
$encrypted
IBoolean flag indicating whether document is protected.
Definition: tcpdf.php:878
SetAutoPageBreak($auto, $margin=0)
Enables or disables the automatic page breaking mode.
Definition: tcpdf.php:3643
getImageRBY()
Return the right-bottom (or left-bottom for RTL) corner Y coordinate of last inserted image...
Definition: tcpdf.php:4277
sendOutputData($data, $length)
Ouput input data and compress it if possible.
Definition: tcpdf.php:8940
writeBarcode($x, $y, $w, $h, $type, $style, $font, $xres, $code)
This function is DEPRECATED, please use the new write1DBarcode() function.
Definition: tcpdf.php:20344
SkewX($angle_x, $x='', $y='')
Skew horizontally.
Definition: tcpdf.php:15188
$svginheritprop
Array of hinheritable SVG properties.
Definition: tcpdf.php:1798
registrationMark($x, $y, $r, $double=false, $cola=array(0, 0, 0), $colb=array(255, 255, 255))
Paints a registration mark.
Definition: tcpdf.php:19122
$barcode
Barcode to print on page footer (only if set).
Definition: tcpdf.php:696
$linestyleDash
PDF string for dash value of the last line.
Definition: tcpdf.php:1112
getCSSFontStretching($stretch, $parent=100)
Returns the percentage of font stretching from CSS value.
Definition: tcpdf.php:21395
$htmlvspace
Count the latest inserted vertical spaces on HTML.
Definition: tcpdf.php:1126
setFooterFont($font)
Set footer font.
Definition: tcpdf.php:13955
$feps
Epsilon value used for float calculations.
Definition: tcpdf.php:1175
addEmptySignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1)
Add an empty digital signature appearance (a cliccable rectangle area to get signature properties) ...
Definition: tcpdf.php:18413
_getFWORD($str, $offset)
Get FWORD from string (Big Endian 16-bit signed integer).
Definition: tcpdf.php:10261
UTF8StringToArray($str)
Converts UTF-8 strings to codepoints array.
Definition: tcpdf.php:13680
getAlpha()
Get the alpha mode array (CA, ca, BM, AIS).
Definition: tcpdf.php:18866
getGroupPageNoFormatted()
Returns the current group page number formatted as a string.
Definition: tcpdf.php:18583
getDestination()
Return the Named Destination array.
Definition: tcpdf.php:16851
_dooverline($x, $y, $txt)
Overline text.
Definition: tcpdf.php:13389
SetTextColorArray($color, $ret=false)
Defines the color used for text.
Definition: tcpdf.php:4787
$listindentlevel
HTML PARSER: current list indententation level.
Definition: tcpdf.php:823
putHtmlListBullet($listdepth, $listtype='', $size=10)
Output an HTML list bullet or ordered item symbol.
Definition: tcpdf.php:25542
writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=false, $reseth=true, $align='', $autopadding=true)
Prints a cell (rectangular area) with optional borders, background color and html text string...
Definition: tcpdf.php:22277
readDiskCache($filename)
Read data from a temporary file on filesystem.
Definition: tcpdf.php:25912
intToRoman($number)
Returns the Roman representation of an integer number.
Definition: tcpdf.php:25477
$htmlLinkColorArray
Default color for html links.
Definition: tcpdf.php:1225
$state
Current document state.
Definition: tcpdf.php:209
endTemplate()
End the current XObject Template started with startTemplate() and restore the previous graphic state...
Definition: tcpdf.php:27731
_beginpage($orientation='', $format='')
Initialize a new page.
Definition: tcpdf.php:13256
Header()
This method is used to render the page header.
Definition: tcpdf.php:4303
$PDFVersion
PDF version.
Definition: tcpdf.php:634
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 GD library.
Definition: tcpdf.php:8661
$signature_max_length
Digital signature max length.
Definition: tcpdf.php:1379
UTF8ToLatin1($str)
Converts UTF-8 strings to Latin1 when using the standard 14 core fonts.
Definition: tcpdf.php:13815
_getannotsrefs($n)
Get references to page annotations.
Definition: tcpdf.php:9519
getFontSpacing()
Get the amount to increase or decrease the space between characters in a text.
Definition: tcpdf.php:27920
startTemplate($w=0, $h=0, $group=false)
Start a new XObject Template.
Definition: tcpdf.php:27666
getPDFData()
Returns the PDF data.
Definition: tcpdf.php:13988
_out($s)
Output a string to the document.
Definition: tcpdf.php:13623
setTextRenderingMode($stroke=0, $fill=true, $clip=false)
Set Text rendering mode.
Definition: tcpdf.php:27172
AliasNbPages($s='')
This method is DEPRECATED and doesn't have any effect.
Definition: tcpdf.php:18466
GetY()
Returns the ordinate of the current position.
Definition: tcpdf.php:8814
$listindent
HTML PARSER: indent amount for lists.
Definition: tcpdf.php:817
unhtmlentities($text_to_convert)
Reverse function for htmlentities.
Definition: tcpdf.php:14205
getAnnotOptFromJSProp($prop)
Convert JavaScript form fields properties array to Annotation Properties array.
Definition: tcpdf.php:17202
$GLOBALS['page_columns_count']
GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0, $getarray=false)
Returns the length of a string in user unit.
Definition: tcpdf.php:4975
_Ovalue()
Compute O value (used for encryption)
Definition: tcpdf.php:14559
getFontBBox()
Returns the bounding box of the current font in user units.
Definition: tcpdf.php:5448
$viewer_preferences
PDF viewer preferences.
Definition: tcpdf.php:1014
$lisymbol
Symbol used for HTML unordered list items.
Definition: tcpdf.php:1140
$page_obj_id
ID of page objects.
Definition: tcpdf.php:1435
endTOCPage()
Terminate the current TOC (Table Of Content) page.
Definition: tcpdf.php:3998
$force_srgb
If true force sRGB color profile for all document.
Definition: tcpdf.php:1876
replaceChar($oldchar, $newchar)
Replace a char if is defined on the current font.
Definition: tcpdf.php:6483
Bookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0, 0, 0), $x=-1)
Adds a bookmark.
Definition: tcpdf.php:16902
$alias_num_page
String alias for page number.
Definition: tcpdf.php:561
getBreakMargin($pagenum='')
Returns the page break margin.
Definition: tcpdf.php:3386
setHeader()
This method is used to render the page header.
Definition: tcpdf.php:4427
getSVGTransformMatrix($attribute)
Get the tranformation matrix from SVG transform attribute.
Definition: tcpdf.php:28412
$cache_UTF8StringToArray
Cache array for UTF8StringToArray() method.
Definition: tcpdf.php:1344
getPageNumGroupAlias()
Return the alias for the page number on the current page group.
Definition: tcpdf.php:18560
getCSSBorderMargin($cssbspace, $width=0)
Get the border-spacing from CSS attribute.
Definition: tcpdf.php:21330
$header_logo
Header image logo.
Definition: tcpdf.php:714
Translate($t_x, $t_y)
Translate graphic object horizontally and vertically.
Definition: tcpdf.php:15136
getFontBuffer($font)
Get font buffer content.
Definition: tcpdf.php:26125
SetFillColorArray($color, $ret=false)
Defines the color used for all filling operations (filled rectangles and cell backgrounds).
Definition: tcpdf.php:4774
_getTrueTypeFontSubset($font, $subsetchars)
Returns a subset of the TrueType font data without the unused glyphs.
Definition: tcpdf.php:11187
$footer_line_color
Color for footer line (RGB array).
Definition: tcpdf.php:760
_getULONG($str, $offset)
Get ULONG from string (Big Endian 32-bit unsigned integer).
Definition: tcpdf.php:10219
_putresources()
Output Resources.
Definition: tcpdf.php:12682
UTF8ArrSubString($strarr, $start='', $end='')
Extract a slice of the $strarr array and return it as string.
Definition: tcpdf.php:7712
SetLink($link, $y=0, $page=-1)
Defines the page and position a link points to.
Definition: tcpdf.php:5646
$num_columns
Number of colums.
Definition: tcpdf.php:1540
GetAbsX()
Returns the absolute X value of current position.
Definition: tcpdf.php:8803
setSpotColor($type, $name, $tint=100)
Set the spot color for the specified type ('draw', 'fill', 'text').
Definition: tcpdf.php:4644
setBarcode($bc='')
Set document barcode.
Definition: tcpdf.php:19994
SetBooklet($booklet=true, $inner=-1, $outer=-1)
Set the booklet mode for double-sided pages.
Definition: tcpdf.php:25312
$font_spacing
Increases or decreases the space between characters in a text by the specified amount (tracking)...
Definition: tcpdf.php:1658
formatPageNumber($num)
Format the page numbers.
Definition: tcpdf.php:18594
$listcount
HTML PARSER: array count list items on nested lists.
Definition: tcpdf.php:805
SetCellPadding($pad)
Set the same internal Cell padding for top, right, bottom, left-.
Definition: tcpdf.php:3480
_putviewerpreferences()
Output viewer preferences.
Definition: tcpdf.php:13078
cropMark($x, $y, $w, $h, $type='T, R, B, L', $color=array(0, 0, 0))
Paints crop marks.
Definition: tcpdf.php:19051
drawHTMLTagBorder($tag, $xmax)
Draw an HTML block border and fill.
Definition: tcpdf.php:25066
$opencell
Boolean flag to indicate if the border of the cell sides that cross the page should be removed...
Definition: tcpdf.php:1196
ScaleX($s_x, $x='', $y='')
Horizontal Scaling.
Definition: tcpdf.php:14992
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:18953
Scale($s_x, $s_y, $x='', $y='')
Vertical and horizontal non-proportional Scaling.
Definition: tcpdf.php:15032
Error($msg)
This method is automatically called in case of fatal error; it simply outputs the message and halts t...
Definition: tcpdf.php:3835
getTransformationMatrixProduct($ta, $tb)
Get the product of two SVG tranformation matrices.
Definition: tcpdf.php:28508
getCellHeightRatio()
return the height of cell repect font height.
Definition: tcpdf.php:18908
$theadMargins
Margins used for table header.
Definition: tcpdf.php:1337
isCharDefined($char, $font='', $style='')
Return true in the character is present in the specified font.
Definition: tcpdf.php:5555
$FontSizePt
Current font size in points.
Definition: tcpdf.php:441
MirrorP($x='', $y='')
Point reflection mirroring.
Definition: tcpdf.php:15088
getHyphenPatternsFromTEX($file)
Returns an array of hyphenation patterns.
Definition: tcpdf.php:27357
$header_margin
Minimum distance between header and top page margin.
Definition: tcpdf.php:652
$internal_encoding
PHP internal encoding.
Definition: tcpdf.php:855
getAliasNbPages()
Returns the string alias used for the total number of pages.
Definition: tcpdf.php:18515
$svgdir
Directory used for the last SVG image.
Definition: tcpdf.php:1714
SetFontSize($size, $out=true)
Defines the size of the current font.
Definition: tcpdf.php:5408
global $l
Definition: afr.php:33
setFontSubsetting($enable=true)
Enable or disable default option for font subsetting.
Definition: tcpdf.php:27544
SetRightMargin($margin)
Defines the right margin.
Definition: tcpdf.php:3466
$alias_group_num_page
String alias for group page number.
Definition: tcpdf.php:573
addTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC', $style='', $color=array(0, 0, 0))
Output a Table of Content Index (TOC).
Definition: tcpdf.php:26562
setViewerPreferences($preferences)
Set the viewer preferences dictionary controlling the way the document is to be presented on the scre...
Definition: tcpdf.php:18936
$lispacer
Spacer string for LI tags.
Definition: tcpdf.php:841
_getrawstream($s, $n=0)
get raw output stream.
Definition: tcpdf.php:13588
$TextColor
Commands for text color.
Definition: tcpdf.php:465
$FillColor
Commands for filling color.
Definition: tcpdf.php:459
$font_subsetting
Boolean flag: if true enables font subsetting by default.
Definition: tcpdf.php:1616
StopTransform()
Stops a 2D tranformation restoring previous graphic state.
Definition: tcpdf.php:14967
RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array())
Draws a rounded rectangle.
Definition: tcpdf.php:16035
$n_dests
Object ID for Named Destinations.
Definition: tcpdf.php:1707
SetDocInfoUnicode($unicode=true)
Turn on/off Unicode mode for document information dictionary (meta tags).
Definition: tcpdf.php:3769
getTCPDFVersion()
Return the current TCPDF version.
Definition: tcpdf.php:2148
setFontSpacing($spacing=0)
Set the amount to increase or decrease the space between characters in a text.
Definition: tcpdf.php:27909
$re_spaces
Regular expression used to find blank characters (required for word-wrapping).
Definition: tcpdf.php:1400
_putpages()
Output pages (and replace page number aliases).
Definition: tcpdf.php:9344
_putresourcedict()
Output Resources Dictionary.
Definition: tcpdf.php:12618
setPageRegions($regions=array())
Set no-write regions on page.
Definition: tcpdf.php:27947
$pdfunit
Default unit of measure for document.
Definition: tcpdf.php:1596
$x
Current horizontal position in user unit for cell positioning.
Definition: tcpdf.php:325
$original_rMargin
Original right margin value.
Definition: tcpdf.php:672
UTF8ArrayToUniArray($ta)
Convert an array of UTF8 values to array of unicode characters.
Definition: tcpdf.php:7756
_outPoint($x, $y)
Begin a new subpath by moving the current point to coordinates (x, y), omitting any connecting line s...
Definition: tcpdf.php:15383
swapMargins($reverse=true)
Swap the left and right margins.
Definition: tcpdf.php:25328
$maxselcol
Maximum page and column selected.
Definition: tcpdf.php:1561
getOriginalMargins()
Returns an array containing original margins:
Definition: tcpdf.php:20662
$filename
$textrendermode
Text rendering mode: 0 = Fill text; 1 = Stroke text; 2 = Fill, then stroke text; 3 = Neither fill nor...
Definition: tcpdf.php:1575
resetColumns()
Remove columns and reset page margins.
Definition: tcpdf.php:27033
$match
Definition: search.php:86
$lMargin
Left margin.
Definition: tcpdf.php:275
$jpeg_quality
Set the default JPEG compression quality (1-100).
Definition: tcpdf.php:1000
getPageWidth($pagenum='')
Returns the page width in units.
Definition: tcpdf.php:3354
pixelsToUnits($px)
Converts pixels to User's Units.
Definition: tcpdf.php:14194
$ColorFlag
Indicates whether fill and text colors are different.
Definition: tcpdf.php:471
revstrpos($haystack, $needle, $offset=0)
Find position of last occurrence of a substring in a string.
Definition: tcpdf.php:26984
$textindent
Text indentation value (used for text-indent CSS attribute).
Definition: tcpdf.php:1505
setPageBoxTypes($boxes)
Set page boxes to be included on page descriptions.
Definition: tcpdf.php:9330
$form_enctype
Current form encryption type (used during XHTML rendering).
Definition: tcpdf.php:1470
PHP class to creates array representations for 2D barcodes to be used with TCPDF (http://www.tcpdf.org).
Definition: 2dbarcodes.php:50
$docinfounicode
If true set the document information dictionary in Unicode.
Definition: tcpdf.php:513
$tmprtl
Boolean flag used to force RTL or LTR string direction.
Definition: tcpdf.php:869
$linestyleCap
PDF string for CAP value of the last line.
Definition: tcpdf.php:1098
setDocCreationTimestamp($time)
Set the document creation timestamp.
Definition: tcpdf.php:13451
setPageUnit($unit)
Set the units of measure for the document.
Definition: tcpdf.php:2158
getFontSubsetting()
Return the default option for font subsetting.
Definition: tcpdf.php:27559
$ZoomMode
Zoom display mode.
Definition: tcpdf.php:501
$svgunit
Deafult unit of measure for SVG.
Definition: tcpdf.php:1721
$DrawColor
Commands for drawing color.
Definition: tcpdf.php:453
Line($x1, $y1, $x2, $y2, $style=array())
Draws a line between two points.
Definition: tcpdf.php:15480
setTableHeader()
This method is used to render the table header on new page (if any).
Definition: tcpdf.php:4532
$cached_files
Array of cached files.
Definition: tcpdf.php:379
checkPageBreak($h=0, $y='', $addpage=true)
Add page if needed.
Definition: tcpdf.php:5868
getColumn()
Return the current column number.
Definition: tcpdf.php:27139
getFormDefaultProp()
Return the default properties for form fields.
Definition: tcpdf.php:17579
isUnicodeFont()
Return true if the current font is unicode type.
Definition: tcpdf.php:27611
$CoreFonts
Array of standard font names.
Definition: tcpdf.php:349
printTemplate($id, $x='', $y='', $w=0, $h=0, $align='', $palign='', $fitonpage=false)
Print an XObject Template.
Definition: tcpdf.php:27760
$xobjects
Array of XObjects.
Definition: tcpdf.php:1630
getGDgamma($c)
Get the GD-corrected PNG gamma value from alpha color.
Definition: tcpdf.php:8725
getFontSizePt()
Returns the current font size in points unit.
Definition: tcpdf.php:20686
isRTLTextDir()
Return the current temporary RTL status.
Definition: tcpdf.php:3273
setDefaultTableColumns($cols=4)
Set the default number of columns in a row for HTML tables.
Definition: tcpdf.php:18889
SetXY($x, $y, $rtloff=false)
Defines the abscissa and ordinate of the current position.
Definition: tcpdf.php:8893
_getUFWORD($str, $offset)
Get UFWORD from string (Big Endian 16-bit unsigned integer).
Definition: tcpdf.php:10278
SetTextSpotColor($name, $tint=100)
Defines the spot color used for text.
Definition: tcpdf.php:4714
$emptypagemrk
Array used to store page positions to track empty pages (keys are the page numbers).
Definition: tcpdf.php:1049
$cache_size_UTF8StringToArray
Current size of cache array used for UTF8StringToArray() method.
Definition: tcpdf.php:1358
setImageSubBuffer($image, $key, $data)
Set image buffer content for a specified sub-key.
Definition: tcpdf.php:26043
$inthead
True when we are printing the thead section on a new page.
Definition: tcpdf.php:1526
addPageRegion($region)
Add a single no-write region on selected page.
Definition: tcpdf.php:27967
$doc_modification_timestamp
Document modification date-time.
Definition: tcpdf.php:1897
$images
Array of used images.
Definition: tcpdf.php:373
utf8StrArrRev($arr, $str='', $setbom=false, $forcertl=false)
Reverse the RLT substrings array using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
Definition: tcpdf.php:16204
$id
Definition: articles.php:22
$svgdefsmode
Boolean value true when in SVG defs group.
Definition: tcpdf.php:1742
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:15638
endPage($tocpage=false)
Terminate the current page.
Definition: tcpdf.php:4037
getCSSdataArray($dom, $key, $css)
Returns the styles array that apply for the selected HTML tag.
Definition: tcpdf.php:21039
getGraphicVars()
Returns current graphic variables as array.
Definition: tcpdf.php:25744
_putXMP()
Put XMP data object and return ID.
Definition: tcpdf.php:12766
setAlpha($stroking=1, $bm='Normal', $nonstroking='', $ais=false)
Set alpha for stroking (CA) and non-stroking (ca) operations.
Definition: tcpdf.php:18834
$FontDescent
Current font descent (distance between font bottom and baseline).
Definition: tcpdf.php:417
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:6709
swapPageBoxCoordinates($page)
Swap X and Y coordinates of page boxes (change page boxes orientation).
Definition: tcpdf.php:3062
getBorderStartPosition()
Return the starting coordinates to draw an html border.
Definition: tcpdf.php:25050
hyphenateWord($word, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8)
Returns an array of chars containing soft hyphens.
Definition: tcpdf.php:27287
_JScolor($color)
Convert color to javascript color.
Definition: tcpdf.php:17143
$InHeader
Flag set when processing page header.
Definition: tcpdf.php:489
rollbackTransaction($self=false)
This method allows to undo the latest transaction by returning the latest saved TCPDF object with sta...
Definition: tcpdf.php:26931
SetKeywords($keywords)
Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'.
Definition: tcpdf.php:3813
getObjFilename($name)
Returns a temporary filename for caching object on filesystem.
Definition: tcpdf.php:25872
$OutlineRoot
Outline root for bookmark.
Definition: tcpdf.php:928
writeDiskCache($filename, $data, $append=false)
Writes data to a temporary file on filesystem.
Definition: tcpdf.php:25884
getSignatureAppearanceArray($x=0, $y=0, $w=0, $h=0, $page=-1)
Get the array that defines the signature appearance (page and rectangle coordinates).
Definition: tcpdf.php:18430
$starting_page_number
Starting page number.
Definition: tcpdf.php:549
$xobjid
Current XObject ID.
Definition: tcpdf.php:1644
_encrypt_data($n, $s)
Encrypt the input string.
Definition: tcpdf.php:14287
$unicode
Object containing unicode data.
Definition: tcpdf.php:619
setPageOrientation($orientation, $autopagebreak='', $bottommargin='')
Set page orientation.
Definition: tcpdf.php:3085
setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array())
Enable document signature (requires the OpenSSL Library).
Definition: tcpdf.php:18362
$clMargin
Cell left margin (used by regions).
Definition: tcpdf.php:287
get_mqr()
Gets the current active configuration setting of magic_quotes_runtime (if the get_magic_quotes_runtim...
Definition: tcpdf.php:8348
setDocModificationTimestamp($time)
Set the document modification timestamp.
Definition: tcpdf.php:13464
setHeaderFont($font)
Set header font.
Definition: tcpdf.php:13935
$alpha
Alpha mode array.
Definition: tcpdf.php:1920
$byterange_string
ByteRange placemark used during digital signature process.
Definition: tcpdf.php:1421
SetAbsX($x)
Set the absolute X coordinate of the current pointer.
Definition: tcpdf.php:8905
setCellPaddings($left='', $top='', $right='', $bottom='')
Set the internal Cell paddings.
Definition: tcpdf.php:3499
getCellCode($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M')
Returns the PDF string code to print a cell (rectangular area) with optional borders, background color and character string.
Definition: tcpdf.php:6015
$page
Current page number.
Definition: tcpdf.php:173
getFontAscent($font, $style='', $size=0)
Return the font ascent value.
Definition: tcpdf.php:5535
$footer_text_color
Color for footer text (RGB array).
Definition: tcpdf.php:753
PHP class for generating PDF documents without requiring external extensions.
Definition: tcpdf.php:157
_datastring($s, $n=0)
Format a data string for meta information.
Definition: tcpdf.php:13437
segSVGContentHandler($parser, $data)
Sets the character data handler function for the XML parser.
Definition: tcpdf.php:29827
commitTransaction()
Delete the copy of the current TCPDF object used for undo operation.
Definition: tcpdf.php:26917
$start_transaction_y
Store Y position when startTransaction() is called.
Definition: tcpdf.php:1519
_getobj($objid='')
Return the starting object string for the selected object ID.
Definition: tcpdf.php:13321
SetSubject($subject)
Defines the subject of the document.
Definition: tcpdf.php:3791
_dolinethroughw($x, $y, $w)
Line through for rectangular text area.
Definition: tcpdf.php:13376
SetFillSpotColor($name, $tint=100)
Defines the spot color used for all filling operations (filled rectangles and cell backgrounds)...
Definition: tcpdf.php:4702
getCellBorder($x, $y, $w, $h, $brd)
Returns the code to draw the cell border.
Definition: tcpdf.php:6506
getCSSBorderStyle($cssborder)
Returns the border style array from CSS border properties.
Definition: tcpdf.php:21171
getAliasNumPage()
Returns the string alias used for the page number.
Definition: tcpdf.php:18530
getOverprint()
Get the overprint mode array (OP, op, OPM).
Definition: tcpdf.php:18821
$numimages
Counts the number of pages.
Definition: tcpdf.php:1253
setOpenCell($isopen)
Set the top/bottom cell sides to be open or closed when the cell cross the page.
Definition: tcpdf.php:25372
convertHexStringToString($bs)
Convert hexadecimal string to string.
Definition: tcpdf.php:14884
setListIndentWidth($width)
Set custom width for list indentation.
Definition: tcpdf.php:25362
Gradient($type, $coords, $stops, $background=array(), $antialias=false)
Output gradient.
Definition: tcpdf.php:19313
getFontsList()
Fill the list of available fonts ($this->fontlist).
Definition: tcpdf.php:5088
$InFooter
Flag set when processing page footer.
Definition: tcpdf.php:495
getImageScale()
Returns the adjusting factor to convert pixels to user units.
Definition: tcpdf.php:3325
setSRGBmode($mode=false)
Set flag to force sRGB_IEC61966-2.1 black scaled ICC color profile for the whole document.
Definition: tcpdf.php:3758
$current_column
Current column number.
Definition: tcpdf.php:1547
_Uvalue()
Compute U value (used for encryption)
Definition: tcpdf.php:14513
$hPt
Current height of page in points.
Definition: tcpdf.php:257
_putspotcolors()
Output Spot Colors Resources.
Definition: tcpdf.php:12586
replacePageNumAliases($page, $replace, $diff=0)
Replace page number aliases with number.
Definition: tcpdf.php:9274
_textstring($s, $n=0)
Format a text string for meta information.
Definition: tcpdf.php:13539
setHtmlLinksStyle($color=array(0, 0, 255), $fontstyle='U')
Set the color and font style for HTML links.
Definition: tcpdf.php:25383
SVGPath($d, $style='')
Draws an SVG path.
Definition: tcpdf.php:28849
getTagStyleFromCSSarray($css)
Compact CSS data array into single string.
Definition: tcpdf.php:21084
unichr($c)
Returns the unicode caracter specified by UTF-8 value.
Definition: tcpdf.php:7768
_toJPEG($image)
Convert the loaded image to a JPEG and then return a structure for the PDF creator.
Definition: tcpdf.php:8366
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:5686
$svgdefs
Array of SVG defs.
Definition: tcpdf.php:1749
__construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false, $pdfa=false)
This is the class constructor.
Definition: tcpdf.php:1967
$re_space
Array of $re_spaces parts.
Definition: tcpdf.php:1407
setCellMargins($left='', $top='', $right='', $bottom='')
Set the internal Cell margins.
Definition: tcpdf.php:3535
getFontStretching()
Get the percentage of character stretching.
Definition: tcpdf.php:27898
$fgcolor
Current foreground color.
Definition: tcpdf.php:793
This is a PHP class containing Font encodings maps class for TCPDF library.
getHeaderFont()
Get header font.
Definition: tcpdf.php:13945
TranslateX($t_x)
Translate graphic object horizontally.
Definition: tcpdf.php:15113
$gdgammacache
Cache array for computed GD gamma values.
Definition: tcpdf.php:1948
setDestination($name, $y=-1, $page='', $x=-1)
Add a Named Destination.
Definition: tcpdf.php:16814
startTransaction()
Stores a copy of the current TCPDF object used for undo operation.
Definition: tcpdf.php:26900
getAllInternalPageNumberAliases()
Return an array containing all internal page aliases.
Definition: tcpdf.php:9257
$tMargin
Top margin.
Definition: tcpdf.php:299
getFooterMargin()
Returns footer margin in user units.
Definition: tcpdf.php:4242
$author
Document author.
Definition: tcpdf.php:531
_putfonts()
Output fonts.
Definition: tcpdf.php:11755

This documentation for Open ecommerce PEEL Shopping and PEEL.fr has been generated by Doxygen on Thu Oct 15 2015 14:30:04 - Peel ecommerce is a product of Agence web Advisto SAS. All rights reserved.