Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/lib/tcpdf/tcpdf.php
Go to the documentation of this file.
00001 <?php
00002 //============================================================+
00003 // File name   : tcpdf.php
00004 // Version     : 5.9.133
00005 // Begin       : 2002-08-03
00006 // Last Update : 2011-10-26
00007 // Author      : Nicola Asuni - Tecnick.com S.r.l - Via Della Pace, 11 - 09044 - Quartucciu (CA) - ITALY - www.tecnick.com - info@tecnick.com
00008 // License     : http://www.tecnick.com/pagefiles/tcpdf/LICENSE.TXT GNU-LGPLv3 + YOU CAN'T REMOVE ANY TCPDF COPYRIGHT NOTICE OR LINK FROM THE GENERATED PDF DOCUMENTS.
00009 // -------------------------------------------------------------------
00010 //
00011 // This file is part of TCPDF software library.
00012 //
00013 // TCPDF is free software: you can redistribute it and/or modify it
00014 // under the terms of the GNU Lesser General Public License as
00015 // published by the Free Software Foundation, either version 3 of the
00016 // License, or (at your option) any later version. Additionally,
00017 // YOU CAN'T REMOVE ANY TCPDF COPYRIGHT NOTICE OR LINK FROM THE
00018 // GENERATED PDF DOCUMENTS.
00019 //
00020 // TCPDF is distributed in the hope that it will be useful, but
00021 // WITHOUT ANY WARRANTY; without even the implied warranty of
00022 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00023 // See the GNU Lesser General Public License for more details.
00024 //
00025 // You should have received a copy of the License
00026 // along with TCPDF. If not, see
00027 // <http://www.tecnick.com/pagefiles/tcpdf/LICENSE.TXT>.
00028 //
00029 // See LICENSE.TXT file for more information.
00030 // -------------------------------------------------------------------
00031 //
00032 // Description :
00033 //   This is a PHP class for generating PDF documents without requiring external extensions.
00034 //
00035 // NOTE:
00036 //   This class was originally derived in 2002 from the Public
00037 //   Domain FPDF class by Olivier Plathey (http://www.fpdf.org),
00038 //   but now is almost entirely rewritten and contains thousands of
00039 //   new lines of code and hundreds new features.
00040 //
00041 // Main features:
00042 //  * no external libraries are required for the basic functions;
00043 //  * all standard page formats, custom page formats, custom margins and units of measure;
00044 //  * UTF-8 Unicode and Right-To-Left languages;
00045 //  * TrueTypeUnicode, TrueType, Type1 and CID-0 fonts;
00046 //  * font subsetting;
00047 //  * methods to publish some XHTML + CSS code, Javascript and Forms;
00048 //  * images, graphic (geometric figures) and transformation methods;
00049 //  * 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)
00050 //  * 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;
00051 //  * JPEG and PNG ICC profiles, Grayscale, RGB, CMYK, Spot Colors and Transparencies;
00052 //  * automatic page header and footer management;
00053 //  * document encryption up to 256 bit and digital signature certifications;
00054 //  * transactions to UNDO commands;
00055 //  * PDF annotations, including links, text and file attachments;
00056 //  * text rendering modes (fill, stroke and clipping);
00057 //  * multiple columns mode;
00058 //  * no-write page regions;
00059 //  * bookmarks, named destinations and table of content;
00060 //  * text hyphenation;
00061 //  * text stretching and spacing (tracking/kerning);
00062 //  * automatic page break, line break and text alignments including justification;
00063 //  * automatic page numbering and page groups;
00064 //  * move and delete pages;
00065 //  * page compression (requires php-zlib extension);
00066 //  * XOBject Templates;
00067 //  * Layers and object visibility.
00068 //      * PDF/A-1b support.
00069 //
00070 // -----------------------------------------------------------
00071 // THANKS TO:
00072 //
00073 // Olivier Plathey (http://www.fpdf.org) for original FPDF.
00074 // Efthimios Mavrogeorgiadis (emavro@yahoo.com) for suggestions on RTL language support.
00075 // Klemen Vodopivec (http://www.fpdf.de/downloads/addons/37/) for Encryption algorithm.
00076 // Warren Sherliker (wsherliker@gmail.com) for better image handling.
00077 // dullus for text Justification.
00078 // Bob Vincent (pillarsdotnet@users.sourceforge.net) for <li> value attribute.
00079 // Patrick Benny for text stretch suggestion on Cell().
00080 // Johannes Güntert for JavaScript support.
00081 // Denis Van Nuffelen for Dynamic Form.
00082 // Jacek Czekaj for multibyte justification
00083 // Anthony Ferrara for the reintroduction of legacy image methods.
00084 // Sourceforge user 1707880 (hucste) for line-trough mode.
00085 // Larry Stanbery for page groups.
00086 // Martin Hall-May for transparency.
00087 // Aaron C. Spike for Polycurve method.
00088 // Mohamad Ali Golkar, Saleh AlMatrafe, Charles Abbott for Arabic and Persian support.
00089 // Moritz Wagner and Andreas Wurmser for graphic functions.
00090 // Andrew Whitehead for core fonts support.
00091 // Esteban Joël Marín for OpenType font conversion.
00092 // Teus Hagen for several suggestions and fixes.
00093 // Yukihiro Nakadaira for CID-0 CJK fonts fixes.
00094 // Kosmas Papachristos for some CSS improvements.
00095 // Marcel Partap for some fixes.
00096 // Won Kyu Park for several suggestions, fixes and patches.
00097 // Dominik Dzienia for QR-code support.
00098 // Laurent Minguet for some suggestions.
00099 // Christian Deligant for some suggestions and fixes.
00100 // Anyone that has reported a bug or sent a suggestion.
00101 //============================================================+
00102 
00143 // Main configuration file. Define the K_TCPDF_EXTERNAL_CONFIG constant to skip this file.
00144 require_once(dirname(__FILE__).'/config/tcpdf_config.php');
00145 
00155 class TCPDF {
00156 
00157         // private properties
00158 
00163         private $tcpdf_version = '5.9.133';
00164 
00165         // Protected properties
00166 
00171         protected $page;
00172 
00177         protected $n;
00178 
00183         protected $offsets;
00184 
00189         protected $buffer;
00190 
00195         protected $pages = array();
00196 
00201         protected $state;
00202 
00207         protected $compress;
00208 
00213         protected $CurOrientation;
00214 
00219         protected $pagedim = array();
00220 
00225         protected $k;
00226 
00231         protected $fwPt;
00232 
00237         protected $fhPt;
00238 
00243         protected $wPt;
00244 
00249         protected $hPt;
00250 
00255         protected $w;
00256 
00261         protected $h;
00262 
00267         protected $lMargin;
00268 
00273         protected $tMargin;
00274 
00279         protected $rMargin;
00280 
00285         protected $bMargin;
00286 
00292         protected $cell_padding = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0);
00293 
00299         protected $cell_margin = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0);
00300 
00305         protected $x;
00306 
00311         protected $y;
00312 
00317         protected $lasth;
00318 
00323         protected $LineWidth;
00324 
00329         protected $CoreFonts;
00330 
00335         protected $fonts = array();
00336 
00341         protected $FontFiles = array();
00342 
00347         protected $diffs = array();
00348 
00353         protected $images = array();
00354 
00359         protected $PageAnnots = array();
00360 
00365         protected $links = array();
00366 
00371         protected $FontFamily;
00372 
00377         protected $FontStyle;
00378 
00384         protected $FontAscent;
00385 
00391         protected $FontDescent;
00392 
00397         protected $underline;
00398 
00403         protected $overline;
00404 
00409         protected $CurrentFont;
00410 
00415         protected $FontSizePt;
00416 
00421         protected $FontSize;
00422 
00427         protected $DrawColor;
00428 
00433         protected $FillColor;
00434 
00439         protected $TextColor;
00440 
00445         protected $ColorFlag;
00446 
00451         protected $AutoPageBreak;
00452 
00457         protected $PageBreakTrigger;
00458 
00463         protected $InHeader = false;
00464 
00469         protected $InFooter = false;
00470 
00475         protected $ZoomMode;
00476 
00481         protected $LayoutMode;
00482 
00487         protected $docinfounicode = true;
00488 
00493         protected $title = '';
00494 
00499         protected $subject = '';
00500 
00505         protected $author = '';
00506 
00511         protected $keywords = '';
00512 
00517         protected $creator = '';
00518 
00523         protected $starting_page_number = 1;
00524 
00529         protected $alias_tot_pages = '{:ptp:}';
00530 
00535         protected $alias_num_page = '{:pnp:}';
00536 
00541         protected $alias_group_tot_pages = '{:ptg:}';
00542 
00547         protected $alias_group_num_page = '{:png:}';
00548 
00553         protected $alias_right_shift = '{rsc:';
00554 
00561         protected $img_rb_x;
00562 
00569         protected $img_rb_y;
00570 
00577         protected $imgscale = 1;
00578 
00585         protected $isunicode = false;
00586 
00593         protected $unicode;
00594 
00601         protected $encmaps;
00602 
00608         protected $PDFVersion = '1.7';
00609 
00614         protected $header_xobjid = -1;
00615 
00620         protected $header_xobj_autoreset = false;
00621 
00626         protected $header_margin;
00627 
00632         protected $footer_margin;
00633 
00639         protected $original_lMargin;
00640 
00646         protected $original_rMargin;
00647 
00652         protected $header_font;
00653 
00658         protected $footer_font;
00659 
00664         protected $l;
00665 
00670         protected $barcode = false;
00671 
00676         protected $print_header = true;
00677 
00682         protected $print_footer = true;
00683 
00688         protected $header_logo = '';
00689 
00694         protected $header_logo_width = 30;
00695 
00700         protected $header_title = '';
00701 
00706         protected $header_string = '';
00707 
00712         protected $default_table_columns = 4;
00713 
00714         // variables for html parser
00715 
00720         protected $HREF = array();
00721 
00726         protected $fontlist = array();
00727 
00732         protected $fgcolor;
00733 
00738         protected $listordered = array();
00739 
00744         protected $listcount = array();
00745 
00750         protected $listnum = 0;
00751 
00756         protected $listindent = 0;
00757 
00762         protected $listindentlevel = 0;
00763 
00768         protected $bgcolor;
00769 
00774         protected $tempfontsize = 10;
00775 
00780         protected $lispacer = '';
00781 
00787         protected $encoding = 'UTF-8';
00788 
00794         protected $internal_encoding;
00795 
00801         protected $rtl = false;
00802 
00808         protected $tmprtl = false;
00809 
00810         // --- Variables used for document encryption:
00811 
00817         protected $encrypted;
00818 
00824         protected $encryptdata = array();
00825 
00831         protected $last_enc_key;
00832 
00838         protected $last_enc_key_c;
00839 
00844         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";
00845 
00851         protected $file_id;
00852 
00853         // --- bookmark ---
00854 
00860         protected $outlines = array();
00861 
00867         protected $OutlineRoot;
00868 
00869         // --- javascript and form ---
00870 
00876         protected $javascript = '';
00877 
00883         protected $n_js;
00884 
00890         protected $linethrough;
00891 
00897         protected $ur = array();
00898 
00904         protected $dpi = 72;
00905 
00911         protected $newpagegroup = array();
00912 
00918         protected $pagegroups = array();
00919 
00925         protected $currpagegroup = 0;
00926 
00932         protected $extgstates;
00933 
00939         protected $jpeg_quality;
00940 
00946         protected $cell_height_ratio = K_CELL_HEIGHT_RATIO;
00947 
00953         protected $viewer_preferences;
00954 
00960         protected $PageMode;
00961 
00967         protected $gradients = array();
00968 
00974         protected $intmrk = array();
00975 
00981         protected $bordermrk = array();
00982 
00988         protected $emptypagemrk = array();
00989 
00995         protected $cntmrk = array();
00996 
01002         protected $footerpos = array();
01003 
01009         protected $footerlen = array();
01010 
01016         protected $newline = true;
01017 
01023         protected $endlinex = 0;
01024 
01030         protected $linestyleWidth = '';
01031 
01037         protected $linestyleCap = '0 J';
01038 
01044         protected $linestyleJoin = '0 j';
01045 
01051         protected $linestyleDash = '[] 0 d';
01052 
01058         protected $openMarkedContent = false;
01059 
01065         protected $htmlvspace = 0;
01066 
01072         protected $spot_colors = array();
01073 
01079         protected $lisymbol = '';
01080 
01086         protected $epsmarker = 'x#!#EPS#!#x';
01087 
01093         protected $transfmatrix = array();
01094 
01100         protected $transfmatrix_key = 0;
01101 
01107         protected $booklet = false;
01108 
01114         protected $feps = 0.005;
01115 
01121         protected $tagvspaces = array();
01122 
01128         protected $customlistindent = -1;
01129 
01135         protected $opencell = true;
01136 
01142         protected $embeddedfiles = array();
01143 
01149         protected $premode = false;
01150 
01157         protected $transfmrk = array();
01158 
01164         protected $htmlLinkColorArray = array(0, 0, 255);
01165 
01171         protected $htmlLinkFontStyle = 'U';
01172 
01178         protected $numpages = 0;
01179 
01185         protected $pagelen = array();
01186 
01192         protected $numimages = 0;
01193 
01199         protected $imagekeys = array();
01200 
01206         protected $bufferlen = 0;
01207 
01213         protected $diskcache = false;
01214 
01220         protected $numfonts = 0;
01221 
01227         protected $fontkeys = array();
01228 
01234         protected $font_obj_ids = array();
01235 
01241         protected $pageopen = array();
01242 
01248         protected $default_monospaced_font = 'courier';
01249 
01255         protected $objcopy;
01256 
01262         protected $cache_file_length = array();
01263 
01269         protected $thead = '';
01270 
01276         protected $theadMargins = array();
01277 
01283         protected $cache_UTF8StringToArray = array();
01284 
01290         protected $cache_maxsize_UTF8StringToArray = 8;
01291 
01297         protected $cache_size_UTF8StringToArray = 0;
01298 
01304         protected $sign = false;
01305 
01311         protected $signature_data = array();
01312 
01318         protected $signature_max_length = 11742;
01319 
01325         protected $signature_appearance = array('page' => 1, 'rect' => '0 0 0 0');
01326 
01332         protected $empty_signature_appearance = array();
01333 
01339         protected $re_spaces = '/[^\S\xa0]/';
01340 
01346         protected $re_space = array('p' => '[^\S\xa0]', 'm' => '');
01347 
01353         protected $sig_obj_id = 0;
01354 
01360         protected $byterange_string = '/ByteRange[0 ********** ********** **********]';
01361 
01367         protected $sig_annot_ref = '***SIGANNREF*** 0 R';
01368 
01374         protected $page_obj_id = array();
01375 
01381         protected $form_obj_id = array();
01382 
01388         protected $default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
01389 
01395         protected $js_objects = array();
01396 
01402         protected $form_action = '';
01403 
01409         protected $form_enctype = 'application/x-www-form-urlencoded';
01410 
01416         protected $form_mode = 'post';
01417 
01423         protected $annotation_fonts = array();
01424 
01430         protected $radiobutton_groups = array();
01431 
01437         protected $radio_groups = array();
01438 
01444         protected $textindent = 0;
01445 
01451         protected $start_transaction_page = 0;
01452 
01458         protected $start_transaction_y = 0;
01459 
01465         protected $inthead = false;
01466 
01472         protected $columns = array();
01473 
01479         protected $num_columns = 1;
01480 
01486         protected $current_column = 0;
01487 
01493         protected $column_start_page = 0;
01494 
01500         protected $maxselcol = array('page' => 0, 'column' => 0);
01501 
01507         protected $colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
01508 
01514         protected $textrendermode = 0;
01515 
01521         protected $textstrokewidth = 0;
01522 
01528         protected $strokecolor;
01529 
01535         protected $pdfunit = 'mm';
01536 
01541         protected $tocpage = false;
01542 
01548         protected $rasterize_vector_images = false;
01549 
01555         protected $font_subsetting = true;
01556 
01562         protected $default_graphic_vars = array();
01563 
01569         protected $xobjects = array();
01570 
01576         protected $inxobj = false;
01577 
01583         protected $xobjid = '';
01584 
01590         protected $font_stretching = 100;
01591 
01597         protected $font_spacing = 0;
01598 
01605         protected $page_regions = array();
01606 
01612         protected $webcolor = array();
01613 
01619         protected $spotcolor = array();
01620 
01626         protected $pdflayers = array();
01627 
01633         protected $dests = array();
01634 
01640         protected $n_dests;
01641 
01647         protected $svgdir = '';
01648 
01654         protected $svgunit = 'px';
01655 
01661         protected $svggradients = array();
01662 
01668         protected $svggradientid = 0;
01669 
01675         protected $svgdefsmode = false;
01676 
01682         protected $svgdefs = array();
01683 
01689         protected $svgclipmode = false;
01690 
01696         protected $svgclippaths = array();
01697 
01703         protected $svgcliptm = array();
01704 
01710         protected $svgclipid = 0;
01711 
01717         protected $svgtext = '';
01718 
01724         protected $svgtextmode = array();
01725 
01731         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');
01732 
01738         protected $svgstyles = array(array(
01739                 'alignment-baseline' => 'auto',
01740                 'baseline-shift' => 'baseline',
01741                 'clip' => 'auto',
01742                 'clip-path' => 'none',
01743                 'clip-rule' => 'nonzero',
01744                 'color' => 'black',
01745                 'color-interpolation' => 'sRGB',
01746                 'color-interpolation-filters' => 'linearRGB',
01747                 'color-profile' => 'auto',
01748                 'color-rendering' => 'auto',
01749                 'cursor' => 'auto',
01750                 'direction' => 'ltr',
01751                 'display' => 'inline',
01752                 'dominant-baseline' => 'auto',
01753                 'enable-background' => 'accumulate',
01754                 'fill' => 'black',
01755                 'fill-opacity' => 1,
01756                 'fill-rule' => 'nonzero',
01757                 'filter' => 'none',
01758                 'flood-color' => 'black',
01759                 'flood-opacity' => 1,
01760                 'font' => '',
01761                 'font-family' => 'helvetica',
01762                 'font-size' => 'medium',
01763                 'font-size-adjust' => 'none',
01764                 'font-stretch' => 'normal',
01765                 'font-style' => 'normal',
01766                 'font-variant' => 'normal',
01767                 'font-weight' => 'normal',
01768                 'glyph-orientation-horizontal' => '0deg',
01769                 'glyph-orientation-vertical' => 'auto',
01770                 'image-rendering' => 'auto',
01771                 'kerning' => 'auto',
01772                 'letter-spacing' => 'normal',
01773                 'lighting-color' => 'white',
01774                 'marker' => '',
01775                 'marker-end' => 'none',
01776                 'marker-mid' => 'none',
01777                 'marker-start' => 'none',
01778                 'mask' => 'none',
01779                 'opacity' => 1,
01780                 'overflow' => 'auto',
01781                 'pointer-events' => 'visiblePainted',
01782                 'shape-rendering' => 'auto',
01783                 'stop-color' => 'black',
01784                 'stop-opacity' => 1,
01785                 'stroke' => 'none',
01786                 'stroke-dasharray' => 'none',
01787                 'stroke-dashoffset' => 0,
01788                 'stroke-linecap' => 'butt',
01789                 'stroke-linejoin' => 'miter',
01790                 'stroke-miterlimit' => 4,
01791                 'stroke-opacity' => 1,
01792                 'stroke-width' => 1,
01793                 'text-anchor' => 'start',
01794                 'text-decoration' => 'none',
01795                 'text-rendering' => 'auto',
01796                 'unicode-bidi' => 'normal',
01797                 'visibility' => 'visible',
01798                 'word-spacing' => 'normal',
01799                 'writing-mode' => 'lr-tb',
01800                 'text-color' => 'black',
01801                 'transfmatrix' => array(1, 0, 0, 1, 0, 0)
01802                 ));
01803 
01809         protected $force_srgb = false;
01810 
01816         protected $pdfa_mode = false;
01817 
01823         protected $doc_date;
01824 
01830         protected $custom_xmp = '';
01831 
01832         //------------------------------------------------------------
01833         // METHODS
01834         //------------------------------------------------------------
01835 
01849         public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false, $pdfa=false) {
01850                 /* Set internal character encoding to ASCII */
01851                 if (function_exists('mb_internal_encoding') AND mb_internal_encoding()) {
01852                         $this->internal_encoding = mb_internal_encoding();
01853                         mb_internal_encoding('ASCII');
01854                 }
01855                 // get array of HTML colors
01856                 require(dirname(__FILE__).'/htmlcolors.php');
01857                 $this->webcolor = $webcolor;
01858                 // get array of custom spot colors
01859                 if (file_exists(dirname(__FILE__).'/spotcolors.php')) {
01860                         require(dirname(__FILE__).'/spotcolors.php');
01861                         $this->spotcolor = $spotcolor;
01862                 } else {
01863                         $this->spotcolor = array();
01864                 }
01865                 require_once(dirname(__FILE__).'/unicode_data.php');
01866                 $this->unicode = new TCPDF_UNICODE_DATA();
01867                 require_once(dirname(__FILE__).'/encodings_maps.php');
01868                 $this->encmaps = new TCPDF_ENCODING_MAPS();
01869                 $this->font_obj_ids = array();
01870                 $this->page_obj_id = array();
01871                 $this->form_obj_id = array();
01872                 // set pdf/a mode
01873                 $this->pdfa_mode = $pdfa;
01874                 $this->force_srgb = false;
01875                 // set disk caching
01876                 $this->diskcache = $diskcache ? true : false;
01877                 // set language direction
01878                 $this->rtl = false;
01879                 $this->tmprtl = false;
01880                 // some checks
01881                 $this->_dochecks();
01882                 // initialization of properties
01883                 $this->isunicode = $unicode;
01884                 $this->page = 0;
01885                 $this->transfmrk[0] = array();
01886                 $this->pagedim = array();
01887                 $this->n = 2;
01888                 $this->buffer = '';
01889                 $this->pages = array();
01890                 $this->state = 0;
01891                 $this->fonts = array();
01892                 $this->FontFiles = array();
01893                 $this->diffs = array();
01894                 $this->images = array();
01895                 $this->links = array();
01896                 $this->gradients = array();
01897                 $this->InFooter = false;
01898                 $this->lasth = 0;
01899                 $this->FontFamily = 'helvetica';
01900                 $this->FontStyle = '';
01901                 $this->FontSizePt = 12;
01902                 $this->underline = false;
01903                 $this->overline = false;
01904                 $this->linethrough = false;
01905                 $this->DrawColor = '0 G';
01906                 $this->FillColor = '0 g';
01907                 $this->TextColor = '0 g';
01908                 $this->ColorFlag = false;
01909                 $this->pdflayers = array();
01910                 // encryption values
01911                 $this->encrypted = false;
01912                 $this->last_enc_key = '';
01913                 // standard Unicode fonts
01914                 $this->CoreFonts = array(
01915                         'courier'=>'Courier',
01916                         'courierB'=>'Courier-Bold',
01917                         'courierI'=>'Courier-Oblique',
01918                         'courierBI'=>'Courier-BoldOblique',
01919                         'helvetica'=>'Helvetica',
01920                         'helveticaB'=>'Helvetica-Bold',
01921                         'helveticaI'=>'Helvetica-Oblique',
01922                         'helveticaBI'=>'Helvetica-BoldOblique',
01923                         'times'=>'Times-Roman',
01924                         'timesB'=>'Times-Bold',
01925                         'timesI'=>'Times-Italic',
01926                         'timesBI'=>'Times-BoldItalic',
01927                         'symbol'=>'Symbol',
01928                         'zapfdingbats'=>'ZapfDingbats'
01929                 );
01930                 // set scale factor
01931                 $this->setPageUnit($unit);
01932                 // set page format and orientation
01933                 $this->setPageFormat($format, $orientation);
01934                 // page margins (1 cm)
01935                 $margin = 28.35 / $this->k;
01936                 $this->SetMargins($margin, $margin);
01937                 // internal cell padding
01938                 $cpadding = $margin / 10;
01939                 $this->setCellPaddings($cpadding, 0, $cpadding, 0);
01940                 // cell margins
01941                 $this->setCellMargins(0, 0, 0, 0);
01942                 // line width (0.2 mm)
01943                 $this->LineWidth = 0.57 / $this->k;
01944                 $this->linestyleWidth = sprintf('%.2F w', ($this->LineWidth * $this->k));
01945                 $this->linestyleCap = '0 J';
01946                 $this->linestyleJoin = '0 j';
01947                 $this->linestyleDash = '[] 0 d';
01948                 // automatic page break
01949                 $this->SetAutoPageBreak(true, (2 * $margin));
01950                 // full width display mode
01951                 $this->SetDisplayMode('fullwidth');
01952                 // compression
01953                 $this->SetCompression();
01954                 // set default PDF version number
01955                 $this->setPDFVersion();
01956                 $this->encoding = $encoding;
01957                 $this->HREF = array();
01958                 $this->getFontsList();
01959                 $this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0);
01960                 $this->strokecolor = array('R' => 0, 'G' => 0, 'B' => 0);
01961                 $this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255);
01962                 $this->extgstates = array();
01963                 // user's rights
01964                 $this->sign = false;
01965                 $this->ur['enabled'] = false;
01966                 $this->ur['document'] = '/FullSave';
01967                 $this->ur['annots'] = '/Create/Delete/Modify/Copy/Import/Export';
01968                 $this->ur['form'] = '/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate';
01969                 $this->ur['signature'] = '/Modify';
01970                 $this->ur['ef'] = '/Create/Delete/Modify/Import';
01971                 $this->ur['formex'] = '';
01972                 $this->signature_appearance = array('page' => 1, 'rect' => '0 0 0 0');
01973                 $this->empty_signature_appearance = array();
01974                 // set default JPEG quality
01975                 $this->jpeg_quality = 75;
01976                 // initialize some settings
01977                 $this->utf8Bidi(array(''), '');
01978                 // set default font
01979                 $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
01980                 // check if PCRE Unicode support is enabled
01981                 if ($this->isunicode AND (@preg_match('/\pL/u', 'a') == 1)) {
01982                         // PCRE unicode support is turned ON
01983                         // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
01984                         // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
01985                         // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
01986                         //$this->setSpacesRE('/[^\S\P{Z}\P{Lo}\xa0]/u');
01987                         $this->setSpacesRE('/[^\S\P{Z}\xa0]/u');
01988                 } else {
01989                         // PCRE unicode support is turned OFF
01990                         $this->setSpacesRE('/[^\S\xa0]/');
01991                 }
01992                 $this->default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
01993                 // set file ID for trailer
01994                 $this->file_id = md5($this->getRandomSeed('TCPDF'.$orientation.$unit.$format.$encoding));
01995                 // set document date
01996                 $this->doc_date = substr_replace(date('YmdHisO'), '\'', (0 - 2), 0).'\'';
01997                 // get default graphic vars
01998                 $this->default_graphic_vars = $this->getGraphicVars();
01999                 $this->header_xobj_autoreset = false;
02000                 $this->custom_xmp = '';
02001         }
02002 
02008         public function __destruct() {
02009                 // restore internal encoding
02010                 if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) {
02011                         mb_internal_encoding($this->internal_encoding);
02012                 }
02013                 // unset all class variables
02014                 $this->_destroy(true);
02015         }
02016 
02023         public function getTCPDFVersion() {
02024                 return $this->tcpdf_version;
02025         }
02026 
02033         public function setPageUnit($unit) {
02034                 $unit = strtolower($unit);
02035                 //Set scale factor
02036                 switch ($unit) {
02037                         // points
02038                         case 'px':
02039                         case 'pt': {
02040                                 $this->k = 1;
02041                                 break;
02042                         }
02043                         // millimeters
02044                         case 'mm': {
02045                                 $this->k = $this->dpi / 25.4;
02046                                 break;
02047                         }
02048                         // centimeters
02049                         case 'cm': {
02050                                 $this->k = $this->dpi / 2.54;
02051                                 break;
02052                         }
02053                         // inches
02054                         case 'in': {
02055                                 $this->k = $this->dpi;
02056                                 break;
02057                         }
02058                         // unsupported unit
02059                         default : {
02060                                 $this->Error('Incorrect unit: '.$unit);
02061                                 break;
02062                         }
02063                 }
02064                 $this->pdfunit = $unit;
02065                 if (isset($this->CurOrientation)) {
02066                         $this->setPageOrientation($this->CurOrientation);
02067                 }
02068         }
02069 
02384         public function getPageSizeFromFormat($format) {
02385                 // Paper cordinates are calculated in this way: (inches * 72) where (1 inch = 25.4 mm)
02386                 switch (strtoupper($format)) {
02387                         // ISO 216 A Series + 2 SIS 014711 extensions
02388                         case 'A0' : {$pf = array( 2383.937, 3370.394); break;}
02389                         case 'A1' : {$pf = array( 1683.780, 2383.937); break;}
02390                         case 'A2' : {$pf = array( 1190.551, 1683.780); break;}
02391                         case 'A3' : {$pf = array(  841.890, 1190.551); break;}
02392                         case 'A4' : {$pf = array(  595.276,  841.890); break;}
02393                         case 'A5' : {$pf = array(  419.528,  595.276); break;}
02394                         case 'A6' : {$pf = array(  297.638,  419.528); break;}
02395                         case 'A7' : {$pf = array(  209.764,  297.638); break;}
02396                         case 'A8' : {$pf = array(  147.402,  209.764); break;}
02397                         case 'A9' : {$pf = array(  104.882,  147.402); break;}
02398                         case 'A10': {$pf = array(   73.701,  104.882); break;}
02399                         case 'A11': {$pf = array(   51.024,   73.701); break;}
02400                         case 'A12': {$pf = array(   36.850,   51.024); break;}
02401                         // ISO 216 B Series + 2 SIS 014711 extensions
02402                         case 'B0' : {$pf = array( 2834.646, 4008.189); break;}
02403                         case 'B1' : {$pf = array( 2004.094, 2834.646); break;}
02404                         case 'B2' : {$pf = array( 1417.323, 2004.094); break;}
02405                         case 'B3' : {$pf = array( 1000.630, 1417.323); break;}
02406                         case 'B4' : {$pf = array(  708.661, 1000.630); break;}
02407                         case 'B5' : {$pf = array(  498.898,  708.661); break;}
02408                         case 'B6' : {$pf = array(  354.331,  498.898); break;}
02409                         case 'B7' : {$pf = array(  249.449,  354.331); break;}
02410                         case 'B8' : {$pf = array(  175.748,  249.449); break;}
02411                         case 'B9' : {$pf = array(  124.724,  175.748); break;}
02412                         case 'B10': {$pf = array(   87.874,  124.724); break;}
02413                         case 'B11': {$pf = array(   62.362,   87.874); break;}
02414                         case 'B12': {$pf = array(   42.520,   62.362); break;}
02415                         // ISO 216 C Series + 2 SIS 014711 extensions + 2 EXTENSION
02416                         case 'C0' : {$pf = array( 2599.370, 3676.535); break;}
02417                         case 'C1' : {$pf = array( 1836.850, 2599.370); break;}
02418                         case 'C2' : {$pf = array( 1298.268, 1836.850); break;}
02419                         case 'C3' : {$pf = array(  918.425, 1298.268); break;}
02420                         case 'C4' : {$pf = array(  649.134,  918.425); break;}
02421                         case 'C5' : {$pf = array(  459.213,  649.134); break;}
02422                         case 'C6' : {$pf = array(  323.150,  459.213); break;}
02423                         case 'C7' : {$pf = array(  229.606,  323.150); break;}
02424                         case 'C8' : {$pf = array(  161.575,  229.606); break;}
02425                         case 'C9' : {$pf = array(  113.386,  161.575); break;}
02426                         case 'C10': {$pf = array(   79.370,  113.386); break;}
02427                         case 'C11': {$pf = array(   56.693,   79.370); break;}
02428                         case 'C12': {$pf = array(   39.685,   56.693); break;}
02429                         case 'C76': {$pf = array(  229.606,  459.213); break;}
02430                         case 'DL' : {$pf = array(  311.811,  623.622); break;}
02431                         // SIS 014711 E Series
02432                         case 'E0' : {$pf = array( 2491.654, 3517.795); break;}
02433                         case 'E1' : {$pf = array( 1757.480, 2491.654); break;}
02434                         case 'E2' : {$pf = array( 1247.244, 1757.480); break;}
02435                         case 'E3' : {$pf = array(  878.740, 1247.244); break;}
02436                         case 'E4' : {$pf = array(  623.622,  878.740); break;}
02437                         case 'E5' : {$pf = array(  439.370,  623.622); break;}
02438                         case 'E6' : {$pf = array(  311.811,  439.370); break;}
02439                         case 'E7' : {$pf = array(  221.102,  311.811); break;}
02440                         case 'E8' : {$pf = array(  155.906,  221.102); break;}
02441                         case 'E9' : {$pf = array(  110.551,  155.906); break;}
02442                         case 'E10': {$pf = array(   76.535,  110.551); break;}
02443                         case 'E11': {$pf = array(   53.858,   76.535); break;}
02444                         case 'E12': {$pf = array(   36.850,   53.858); break;}
02445                         // SIS 014711 G Series
02446                         case 'G0' : {$pf = array( 2715.591, 3838.110); break;}
02447                         case 'G1' : {$pf = array( 1919.055, 2715.591); break;}
02448                         case 'G2' : {$pf = array( 1357.795, 1919.055); break;}
02449                         case 'G3' : {$pf = array(  958.110, 1357.795); break;}
02450                         case 'G4' : {$pf = array(  677.480,  958.110); break;}
02451                         case 'G5' : {$pf = array(  479.055,  677.480); break;}
02452                         case 'G6' : {$pf = array(  337.323,  479.055); break;}
02453                         case 'G7' : {$pf = array(  238.110,  337.323); break;}
02454                         case 'G8' : {$pf = array(  167.244,  238.110); break;}
02455                         case 'G9' : {$pf = array(  119.055,  167.244); break;}
02456                         case 'G10': {$pf = array(   82.205,  119.055); break;}
02457                         case 'G11': {$pf = array(   59.528,   82.205); break;}
02458                         case 'G12': {$pf = array(   39.685,   59.528); break;}
02459                         // ISO Press
02460                         case 'RA0': {$pf = array( 2437.795, 3458.268); break;}
02461                         case 'RA1': {$pf = array( 1729.134, 2437.795); break;}
02462                         case 'RA2': {$pf = array( 1218.898, 1729.134); break;}
02463                         case 'RA3': {$pf = array(  864.567, 1218.898); break;}
02464                         case 'RA4': {$pf = array(  609.449,  864.567); break;}
02465                         case 'SRA0': {$pf = array( 2551.181, 3628.346); break;}
02466                         case 'SRA1': {$pf = array( 1814.173, 2551.181); break;}
02467                         case 'SRA2': {$pf = array( 1275.591, 1814.173); break;}
02468                         case 'SRA3': {$pf = array(  907.087, 1275.591); break;}
02469                         case 'SRA4': {$pf = array(  637.795,  907.087); break;}
02470                         // German  DIN 476
02471                         case '4A0': {$pf = array( 4767.874, 6740.787); break;}
02472                         case '2A0': {$pf = array( 3370.394, 4767.874); break;}
02473                         // Variations on the ISO Standard
02474                         case 'A2_EXTRA'   : {$pf = array( 1261.417, 1754.646); break;}
02475                         case 'A3+'        : {$pf = array(  932.598, 1369.134); break;}
02476                         case 'A3_EXTRA'   : {$pf = array(  912.756, 1261.417); break;}
02477                         case 'A3_SUPER'   : {$pf = array(  864.567, 1440.000); break;}
02478                         case 'SUPER_A3'   : {$pf = array(  864.567, 1380.472); break;}
02479                         case 'A4_EXTRA'   : {$pf = array(  666.142,  912.756); break;}
02480                         case 'A4_SUPER'   : {$pf = array(  649.134,  912.756); break;}
02481                         case 'SUPER_A4'   : {$pf = array(  643.465, 1009.134); break;}
02482                         case 'A4_LONG'    : {$pf = array(  595.276,  986.457); break;}
02483                         case 'F4'         : {$pf = array(  595.276,  935.433); break;}
02484                         case 'SO_B5_EXTRA': {$pf = array(  572.598,  782.362); break;}
02485                         case 'A5_EXTRA'   : {$pf = array(  490.394,  666.142); break;}
02486                         // ANSI Series
02487                         case 'ANSI_E': {$pf = array( 2448.000, 3168.000); break;}
02488                         case 'ANSI_D': {$pf = array( 1584.000, 2448.000); break;}
02489                         case 'ANSI_C': {$pf = array( 1224.000, 1584.000); break;}
02490                         case 'ANSI_B': {$pf = array(  792.000, 1224.000); break;}
02491                         case 'ANSI_A': {$pf = array(  612.000,  792.000); break;}
02492                         // Traditional 'Loose' North American Paper Sizes
02493                         case 'USLEDGER':
02494                         case 'LEDGER' : {$pf = array( 1224.000,  792.000); break;}
02495                         case 'ORGANIZERK':
02496                         case 'BIBLE':
02497                         case 'USTABLOID':
02498                         case 'TABLOID': {$pf = array(  792.000, 1224.000); break;}
02499                         case 'ORGANIZERM':
02500                         case 'USLETTER':
02501                         case 'LETTER' : {$pf = array(  612.000,  792.000); break;}
02502                         case 'USLEGAL':
02503                         case 'LEGAL'  : {$pf = array(  612.000, 1008.000); break;}
02504                         case 'GOVERNMENTLETTER':
02505                         case 'GLETTER': {$pf = array(  576.000,  756.000); break;}
02506                         case 'JUNIORLEGAL':
02507                         case 'JLEGAL' : {$pf = array(  576.000,  360.000); break;}
02508                         // Other North American Paper Sizes
02509                         case 'QUADDEMY': {$pf = array( 2520.000, 3240.000); break;}
02510                         case 'SUPER_B': {$pf = array(  936.000, 1368.000); break;}
02511                         case 'QUARTO': {$pf = array(  648.000,  792.000); break;}
02512                         case 'GOVERNMENTLEGAL':
02513                         case 'FOLIO': {$pf = array(  612.000,  936.000); break;}
02514                         case 'MONARCH':
02515                         case 'EXECUTIVE': {$pf = array(  522.000,  756.000); break;}
02516                         case 'ORGANIZERL':
02517                         case 'STATEMENT':
02518                         case 'MEMO': {$pf = array(  396.000,  612.000); break;}
02519                         case 'FOOLSCAP': {$pf = array(  595.440,  936.000); break;}
02520                         case 'COMPACT': {$pf = array(  306.000,  486.000); break;}
02521                         case 'ORGANIZERJ': {$pf = array(  198.000,  360.000); break;}
02522                         // Canadian standard CAN 2-9.60M
02523                         case 'P1': {$pf = array( 1587.402, 2437.795); break;}
02524                         case 'P2': {$pf = array( 1218.898, 1587.402); break;}
02525                         case 'P3': {$pf = array(  793.701, 1218.898); break;}
02526                         case 'P4': {$pf = array(  609.449,  793.701); break;}
02527                         case 'P5': {$pf = array(  396.850,  609.449); break;}
02528                         case 'P6': {$pf = array(  303.307,  396.850); break;}
02529                         // North American Architectural Sizes
02530                         case 'ARCH_E' : {$pf = array( 2592.000, 3456.000); break;}
02531                         case 'ARCH_E1': {$pf = array( 2160.000, 3024.000); break;}
02532                         case 'ARCH_D' : {$pf = array( 1728.000, 2592.000); break;}
02533                         case 'BROADSHEET':
02534                         case 'ARCH_C' : {$pf = array( 1296.000, 1728.000); break;}
02535                         case 'ARCH_B' : {$pf = array(  864.000, 1296.000); break;}
02536                         case 'ARCH_A' : {$pf = array(  648.000,  864.000); break;}
02537                         // --- North American Envelope Sizes ---
02538                         //   - Announcement Envelopes
02539                         case 'ANNENV_A2'  : {$pf = array(  314.640,  414.000); break;}
02540                         case 'ANNENV_A6'  : {$pf = array(  342.000,  468.000); break;}
02541                         case 'ANNENV_A7'  : {$pf = array(  378.000,  522.000); break;}
02542                         case 'ANNENV_A8'  : {$pf = array(  396.000,  584.640); break;}
02543                         case 'ANNENV_A10' : {$pf = array(  450.000,  692.640); break;}
02544                         case 'ANNENV_SLIM': {$pf = array(  278.640,  638.640); break;}
02545                         //   - Commercial Envelopes
02546                         case 'COMMENV_N6_1/4': {$pf = array(  252.000,  432.000); break;}
02547                         case 'COMMENV_N6_3/4': {$pf = array(  260.640,  468.000); break;}
02548                         case 'COMMENV_N8'    : {$pf = array(  278.640,  540.000); break;}
02549                         case 'COMMENV_N9'    : {$pf = array(  278.640,  638.640); break;}
02550                         case 'COMMENV_N10'   : {$pf = array(  296.640,  684.000); break;}
02551                         case 'COMMENV_N11'   : {$pf = array(  324.000,  746.640); break;}
02552                         case 'COMMENV_N12'   : {$pf = array(  342.000,  792.000); break;}
02553                         case 'COMMENV_N14'   : {$pf = array(  360.000,  828.000); break;}
02554                         //   - Catalogue Envelopes
02555                         case 'CATENV_N1'     : {$pf = array(  432.000,  648.000); break;}
02556                         case 'CATENV_N1_3/4' : {$pf = array(  468.000,  684.000); break;}
02557                         case 'CATENV_N2'     : {$pf = array(  468.000,  720.000); break;}
02558                         case 'CATENV_N3'     : {$pf = array(  504.000,  720.000); break;}
02559                         case 'CATENV_N6'     : {$pf = array(  540.000,  756.000); break;}
02560                         case 'CATENV_N7'     : {$pf = array(  576.000,  792.000); break;}
02561                         case 'CATENV_N8'     : {$pf = array(  594.000,  810.000); break;}
02562                         case 'CATENV_N9_1/2' : {$pf = array(  612.000,  756.000); break;}
02563                         case 'CATENV_N9_3/4' : {$pf = array(  630.000,  810.000); break;}
02564                         case 'CATENV_N10_1/2': {$pf = array(  648.000,  864.000); break;}
02565                         case 'CATENV_N12_1/2': {$pf = array(  684.000,  900.000); break;}
02566                         case 'CATENV_N13_1/2': {$pf = array(  720.000,  936.000); break;}
02567                         case 'CATENV_N14_1/4': {$pf = array(  810.000,  882.000); break;}
02568                         case 'CATENV_N14_1/2': {$pf = array(  828.000, 1044.000); break;}
02569                         // Japanese (JIS P 0138-61) Standard B-Series
02570                         case 'JIS_B0' : {$pf = array( 2919.685, 4127.244); break;}
02571                         case 'JIS_B1' : {$pf = array( 2063.622, 2919.685); break;}
02572                         case 'JIS_B2' : {$pf = array( 1459.843, 2063.622); break;}
02573                         case 'JIS_B3' : {$pf = array( 1031.811, 1459.843); break;}
02574                         case 'JIS_B4' : {$pf = array(  728.504, 1031.811); break;}
02575                         case 'JIS_B5' : {$pf = array(  515.906,  728.504); break;}
02576                         case 'JIS_B6' : {$pf = array(  362.835,  515.906); break;}
02577                         case 'JIS_B7' : {$pf = array(  257.953,  362.835); break;}
02578                         case 'JIS_B8' : {$pf = array(  181.417,  257.953); break;}
02579                         case 'JIS_B9' : {$pf = array(  127.559,  181.417); break;}
02580                         case 'JIS_B10': {$pf = array(   90.709,  127.559); break;}
02581                         case 'JIS_B11': {$pf = array(   62.362,   90.709); break;}
02582                         case 'JIS_B12': {$pf = array(   45.354,   62.362); break;}
02583                         // PA Series
02584                         case 'PA0' : {$pf = array( 2381.102, 3174.803,); break;}
02585                         case 'PA1' : {$pf = array( 1587.402, 2381.102); break;}
02586                         case 'PA2' : {$pf = array( 1190.551, 1587.402); break;}
02587                         case 'PA3' : {$pf = array(  793.701, 1190.551); break;}
02588                         case 'PA4' : {$pf = array(  595.276,  793.701); break;}
02589                         case 'PA5' : {$pf = array(  396.850,  595.276); break;}
02590                         case 'PA6' : {$pf = array(  297.638,  396.850); break;}
02591                         case 'PA7' : {$pf = array(  198.425,  297.638); break;}
02592                         case 'PA8' : {$pf = array(  147.402,  198.425); break;}
02593                         case 'PA9' : {$pf = array(   99.213,  147.402); break;}
02594                         case 'PA10': {$pf = array(   73.701,   99.213); break;}
02595                         // Standard Photographic Print Sizes
02596                         case 'PASSPORT_PHOTO': {$pf = array(   99.213,  127.559); break;}
02597                         case 'E'   : {$pf = array(  233.858,  340.157); break;}
02598                         case 'L':
02599                         case '3R'  : {$pf = array(  252.283,  360.000); break;}
02600                         case 'KG':
02601                         case '4R'  : {$pf = array(  289.134,  430.866); break;}
02602                         case '4D'  : {$pf = array(  340.157,  430.866); break;}
02603                         case '2L':
02604                         case '5R'  : {$pf = array(  360.000,  504.567); break;}
02605                         case '8P':
02606                         case '6R'  : {$pf = array(  430.866,  575.433); break;}
02607                         case '6P':
02608                         case '8R'  : {$pf = array(  575.433,  720.000); break;}
02609                         case '6PW':
02610                         case 'S8R' : {$pf = array(  575.433,  864.567); break;}
02611                         case '4P':
02612                         case '10R' : {$pf = array(  720.000,  864.567); break;}
02613                         case '4PW':
02614                         case 'S10R': {$pf = array(  720.000, 1080.000); break;}
02615                         case '11R' : {$pf = array(  790.866, 1009.134); break;}
02616                         case 'S11R': {$pf = array(  790.866, 1224.567); break;}
02617                         case '12R' : {$pf = array(  864.567, 1080.000); break;}
02618                         case 'S12R': {$pf = array(  864.567, 1292.598); break;}
02619                         // Common Newspaper Sizes
02620                         case 'NEWSPAPER_BROADSHEET': {$pf = array( 2125.984, 1700.787); break;}
02621                         case 'NEWSPAPER_BERLINER'  : {$pf = array( 1332.283,  892.913); break;}
02622                         case 'NEWSPAPER_TABLOID':
02623                         case 'NEWSPAPER_COMPACT'   : {$pf = array( 1218.898,  793.701); break;}
02624                         // Business Cards
02625                         case 'CREDIT_CARD':
02626                         case 'BUSINESS_CARD':
02627                         case 'BUSINESS_CARD_ISO7810': {$pf = array(  153.014,  242.646); break;}
02628                         case 'BUSINESS_CARD_ISO216' : {$pf = array(  147.402,  209.764); break;}
02629                         case 'BUSINESS_CARD_IT':
02630                         case 'BUSINESS_CARD_UK':
02631                         case 'BUSINESS_CARD_FR':
02632                         case 'BUSINESS_CARD_DE':
02633                         case 'BUSINESS_CARD_ES'     : {$pf = array(  155.906,  240.945); break;}
02634                         case 'BUSINESS_CARD_CA':
02635                         case 'BUSINESS_CARD_US'     : {$pf = array(  144.567,  252.283); break;}
02636                         case 'BUSINESS_CARD_JP'     : {$pf = array(  155.906,  257.953); break;}
02637                         case 'BUSINESS_CARD_HK'     : {$pf = array(  153.071,  255.118); break;}
02638                         case 'BUSINESS_CARD_AU':
02639                         case 'BUSINESS_CARD_DK':
02640                         case 'BUSINESS_CARD_SE'     : {$pf = array(  155.906,  255.118); break;}
02641                         case 'BUSINESS_CARD_RU':
02642                         case 'BUSINESS_CARD_CZ':
02643                         case 'BUSINESS_CARD_FI':
02644                         case 'BUSINESS_CARD_HU':
02645                         case 'BUSINESS_CARD_IL'     : {$pf = array(  141.732,  255.118); break;}
02646                         // Billboards
02647                         case '4SHEET' : {$pf = array( 2880.000, 4320.000); break;}
02648                         case '6SHEET' : {$pf = array( 3401.575, 5102.362); break;}
02649                         case '12SHEET': {$pf = array( 8640.000, 4320.000); break;}
02650                         case '16SHEET': {$pf = array( 5760.000, 8640.000); break;}
02651                         case '32SHEET': {$pf = array(11520.000, 8640.000); break;}
02652                         case '48SHEET': {$pf = array(17280.000, 8640.000); break;}
02653                         case '64SHEET': {$pf = array(23040.000, 8640.000); break;}
02654                         case '96SHEET': {$pf = array(34560.000, 8640.000); break;}
02655                         // Old European Sizes
02656                         //   - Old Imperial English Sizes
02657                         case 'EN_EMPEROR'          : {$pf = array( 3456.000, 5184.000); break;}
02658                         case 'EN_ANTIQUARIAN'      : {$pf = array( 2232.000, 3816.000); break;}
02659                         case 'EN_GRAND_EAGLE'      : {$pf = array( 2070.000, 3024.000); break;}
02660                         case 'EN_DOUBLE_ELEPHANT'  : {$pf = array( 1926.000, 2880.000); break;}
02661                         case 'EN_ATLAS'            : {$pf = array( 1872.000, 2448.000); break;}
02662                         case 'EN_COLOMBIER'        : {$pf = array( 1692.000, 2484.000); break;}
02663                         case 'EN_ELEPHANT'         : {$pf = array( 1656.000, 2016.000); break;}
02664                         case 'EN_DOUBLE_DEMY'      : {$pf = array( 1620.000, 2556.000); break;}
02665                         case 'EN_IMPERIAL'         : {$pf = array( 1584.000, 2160.000); break;}
02666                         case 'EN_PRINCESS'         : {$pf = array( 1548.000, 2016.000); break;}
02667                         case 'EN_CARTRIDGE'        : {$pf = array( 1512.000, 1872.000); break;}
02668                         case 'EN_DOUBLE_LARGE_POST': {$pf = array( 1512.000, 2376.000); break;}
02669                         case 'EN_ROYAL'            : {$pf = array( 1440.000, 1800.000); break;}
02670                         case 'EN_SHEET':
02671                         case 'EN_HALF_POST'        : {$pf = array( 1404.000, 1692.000); break;}
02672                         case 'EN_SUPER_ROYAL'      : {$pf = array( 1368.000, 1944.000); break;}
02673                         case 'EN_DOUBLE_POST'      : {$pf = array( 1368.000, 2196.000); break;}
02674                         case 'EN_MEDIUM'           : {$pf = array( 1260.000, 1656.000); break;}
02675                         case 'EN_DEMY'             : {$pf = array( 1260.000, 1620.000); break;}
02676                         case 'EN_LARGE_POST'       : {$pf = array( 1188.000, 1512.000); break;}
02677                         case 'EN_COPY_DRAUGHT'     : {$pf = array( 1152.000, 1440.000); break;}
02678                         case 'EN_POST'             : {$pf = array( 1116.000, 1386.000); break;}
02679                         case 'EN_CROWN'            : {$pf = array( 1080.000, 1440.000); break;}
02680                         case 'EN_PINCHED_POST'     : {$pf = array( 1062.000, 1332.000); break;}
02681                         case 'EN_BRIEF'            : {$pf = array(  972.000, 1152.000); break;}
02682                         case 'EN_FOOLSCAP'         : {$pf = array(  972.000, 1224.000); break;}
02683                         case 'EN_SMALL_FOOLSCAP'   : {$pf = array(  954.000, 1188.000); break;}
02684                         case 'EN_POTT'             : {$pf = array(  900.000, 1080.000); break;}
02685                         //   - Old Imperial Belgian Sizes
02686                         case 'BE_GRAND_AIGLE' : {$pf = array( 1984.252, 2948.031); break;}
02687                         case 'BE_COLOMBIER'   : {$pf = array( 1757.480, 2409.449); break;}
02688                         case 'BE_DOUBLE_CARRE': {$pf = array( 1757.480, 2607.874); break;}
02689                         case 'BE_ELEPHANT'    : {$pf = array( 1746.142, 2182.677); break;}
02690                         case 'BE_PETIT_AIGLE' : {$pf = array( 1700.787, 2381.102); break;}
02691                         case 'BE_GRAND_JESUS' : {$pf = array( 1559.055, 2069.291); break;}
02692                         case 'BE_JESUS'       : {$pf = array( 1530.709, 2069.291); break;}
02693                         case 'BE_RAISIN'      : {$pf = array( 1417.323, 1842.520); break;}
02694                         case 'BE_GRAND_MEDIAN': {$pf = array( 1303.937, 1714.961); break;}
02695                         case 'BE_DOUBLE_POSTE': {$pf = array( 1233.071, 1601.575); break;}
02696                         case 'BE_COQUILLE'    : {$pf = array( 1218.898, 1587.402); break;}
02697                         case 'BE_PETIT_MEDIAN': {$pf = array( 1176.378, 1502.362); break;}
02698                         case 'BE_RUCHE'       : {$pf = array( 1020.472, 1303.937); break;}
02699                         case 'BE_PROPATRIA'   : {$pf = array(  977.953, 1218.898); break;}
02700                         case 'BE_LYS'         : {$pf = array(  898.583, 1125.354); break;}
02701                         case 'BE_POT'         : {$pf = array(  870.236, 1088.504); break;}
02702                         case 'BE_ROSETTE'     : {$pf = array(  765.354,  983.622); break;}
02703                         //   - Old Imperial French Sizes
02704                         case 'FR_UNIVERS'          : {$pf = array( 2834.646, 3685.039); break;}
02705                         case 'FR_DOUBLE_COLOMBIER' : {$pf = array( 2551.181, 3571.654); break;}
02706                         case 'FR_GRANDE_MONDE'     : {$pf = array( 2551.181, 3571.654); break;}
02707                         case 'FR_DOUBLE_SOLEIL'    : {$pf = array( 2267.717, 3401.575); break;}
02708                         case 'FR_DOUBLE_JESUS'     : {$pf = array( 2154.331, 3174.803); break;}
02709                         case 'FR_GRAND_AIGLE'      : {$pf = array( 2125.984, 3004.724); break;}
02710                         case 'FR_PETIT_AIGLE'      : {$pf = array( 1984.252, 2664.567); break;}
02711                         case 'FR_DOUBLE_RAISIN'    : {$pf = array( 1842.520, 2834.646); break;}
02712                         case 'FR_JOURNAL'          : {$pf = array( 1842.520, 2664.567); break;}
02713                         case 'FR_COLOMBIER_AFFICHE': {$pf = array( 1785.827, 2551.181); break;}
02714                         case 'FR_DOUBLE_CAVALIER'  : {$pf = array( 1757.480, 2607.874); break;}
02715                         case 'FR_CLOCHE'           : {$pf = array( 1700.787, 2267.717); break;}
02716                         case 'FR_SOLEIL'           : {$pf = array( 1700.787, 2267.717); break;}
02717                         case 'FR_DOUBLE_CARRE'     : {$pf = array( 1587.402, 2551.181); break;}
02718                         case 'FR_DOUBLE_COQUILLE'  : {$pf = array( 1587.402, 2494.488); break;}
02719                         case 'FR_JESUS'            : {$pf = array( 1587.402, 2154.331); break;}
02720                         case 'FR_RAISIN'           : {$pf = array( 1417.323, 1842.520); break;}
02721                         case 'FR_CAVALIER'         : {$pf = array( 1303.937, 1757.480); break;}
02722                         case 'FR_DOUBLE_COURONNE'  : {$pf = array( 1303.937, 2040.945); break;}
02723                         case 'FR_CARRE'            : {$pf = array( 1275.591, 1587.402); break;}
02724                         case 'FR_COQUILLE'         : {$pf = array( 1247.244, 1587.402); break;}
02725                         case 'FR_DOUBLE_TELLIERE'  : {$pf = array( 1247.244, 1927.559); break;}
02726                         case 'FR_DOUBLE_CLOCHE'    : {$pf = array( 1133.858, 1700.787); break;}
02727                         case 'FR_DOUBLE_POT'       : {$pf = array( 1133.858, 1757.480); break;}
02728                         case 'FR_ECU'              : {$pf = array( 1133.858, 1474.016); break;}
02729                         case 'FR_COURONNE'         : {$pf = array( 1020.472, 1303.937); break;}
02730                         case 'FR_TELLIERE'         : {$pf = array(  963.780, 1247.244); break;}
02731                         case 'FR_POT'              : {$pf = array(  878.740, 1133.858); break;}
02732                         // DEFAULT ISO A4
02733                         default: {$pf = array(  595.276,  841.890); break;}
02734                 }
02735                 return $pf;
02736         }
02737 
02793         protected function setPageFormat($format, $orientation='P') {
02794                 if (!empty($format) AND isset($this->pagedim[$this->page])) {
02795                         // remove inherited values
02796                         unset($this->pagedim[$this->page]);
02797                 }
02798                 if (is_string($format)) {
02799                         // get page measures from format name
02800                         $pf = $this->getPageSizeFromFormat($format);
02801                         $this->fwPt = $pf[0];
02802                         $this->fhPt = $pf[1];
02803                 } else {
02804                         // the boundaries of the physical medium on which the page shall be displayed or printed
02805                         if (isset($format['MediaBox'])) {
02806                                 $this->setPageBoxes($this->page, 'MediaBox', $format['MediaBox']['llx'], $format['MediaBox']['lly'], $format['MediaBox']['urx'], $format['MediaBox']['ury'], false);
02807                                 $this->fwPt = (($format['MediaBox']['urx'] - $format['MediaBox']['llx']) * $this->k);
02808                                 $this->fhPt = (($format['MediaBox']['ury'] - $format['MediaBox']['lly']) * $this->k);
02809                         } else {
02810                                 if (isset($format[0]) AND is_numeric($format[0]) AND isset($format[1]) AND is_numeric($format[1])) {
02811                                         $pf = array(($format[0] * $this->k), ($format[1] * $this->k));
02812                                 } else {
02813                                         if (!isset($format['format'])) {
02814                                                 // default value
02815                                                 $format['format'] = 'A4';
02816                                         }
02817                                         $pf = $this->getPageSizeFromFormat($format['format']);
02818                                 }
02819                                 $this->fwPt = $pf[0];
02820                                 $this->fhPt = $pf[1];
02821                                 $this->setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true);
02822                         }
02823                         // the visible region of default user space
02824                         if (isset($format['CropBox'])) {
02825                                 $this->setPageBoxes($this->page, 'CropBox', $format['CropBox']['llx'], $format['CropBox']['lly'], $format['CropBox']['urx'], $format['CropBox']['ury'], false);
02826                         }
02827                         // the region to which the contents of the page shall be clipped when output in a production environment
02828                         if (isset($format['BleedBox'])) {
02829                                 $this->setPageBoxes($this->page, 'BleedBox', $format['BleedBox']['llx'], $format['BleedBox']['lly'], $format['BleedBox']['urx'], $format['BleedBox']['ury'], false);
02830                         }
02831                         // the intended dimensions of the finished page after trimming
02832                         if (isset($format['TrimBox'])) {
02833                                 $this->setPageBoxes($this->page, 'TrimBox', $format['TrimBox']['llx'], $format['TrimBox']['lly'], $format['TrimBox']['urx'], $format['TrimBox']['ury'], false);
02834                         }
02835                         // the page's meaningful content (including potential white space)
02836                         if (isset($format['ArtBox'])) {
02837                                 $this->setPageBoxes($this->page, 'ArtBox', $format['ArtBox']['llx'], $format['ArtBox']['lly'], $format['ArtBox']['urx'], $format['ArtBox']['ury'], false);
02838                         }
02839                         // specify the colours and other visual characteristics that should be used in displaying guidelines on the screen for the various page boundaries
02840                         if (isset($format['BoxColorInfo'])) {
02841                                 $this->pagedim[$this->page]['BoxColorInfo'] = $format['BoxColorInfo'];
02842                         }
02843                         if (isset($format['Rotate']) AND (($format['Rotate'] % 90) == 0)) {
02844                                 // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.
02845                                 $this->pagedim[$this->page]['Rotate'] = intval($format['Rotate']);
02846                         }
02847                         if (isset($format['PZ'])) {
02848                                 // The page's preferred zoom (magnification) factor
02849                                 $this->pagedim[$this->page]['PZ'] = floatval($format['PZ']);
02850                         }
02851                         if (isset($format['trans'])) {
02852                                 // The style and duration of the visual transition to use when moving from another page to the given page during a presentation
02853                                 if (isset($format['trans']['Dur'])) {
02854                                         // The page's display duration
02855                                         $this->pagedim[$this->page]['trans']['Dur'] = floatval($format['trans']['Dur']);
02856                                 }
02857                                 $stansition_styles = array('Split', 'Blinds', 'Box', 'Wipe', 'Dissolve', 'Glitter', 'R', 'Fly', 'Push', 'Cover', 'Uncover', 'Fade');
02858                                 if (isset($format['trans']['S']) AND in_array($format['trans']['S'], $stansition_styles)) {
02859                                         // The transition style that shall be used when moving to this page from another during a presentation
02860                                         $this->pagedim[$this->page]['trans']['S'] = $format['trans']['S'];
02861                                         $valid_effect = array('Split', 'Blinds');
02862                                         $valid_vals = array('H', 'V');
02863                                         if (isset($format['trans']['Dm']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['Dm'], $valid_vals)) {
02864                                                 $this->pagedim[$this->page]['trans']['Dm'] = $format['trans']['Dm'];
02865                                         }
02866                                         $valid_effect = array('Split', 'Box', 'Fly');
02867                                         $valid_vals = array('I', 'O');
02868                                         if (isset($format['trans']['M']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['M'], $valid_vals)) {
02869                                                 $this->pagedim[$this->page]['trans']['M'] = $format['trans']['M'];
02870                                         }
02871                                         $valid_effect = array('Wipe', 'Glitter', 'Fly', 'Cover', 'Uncover', 'Push');
02872                                         if (isset($format['trans']['Di']) AND in_array($format['trans']['S'], $valid_effect)) {
02873                                                 if (((($format['trans']['Di'] == 90) OR ($format['trans']['Di'] == 180)) AND ($format['trans']['S'] == 'Wipe'))
02874                                                         OR (($format['trans']['Di'] == 315) AND ($format['trans']['S'] == 'Glitter'))
02875                                                         OR (($format['trans']['Di'] == 0) OR ($format['trans']['Di'] == 270))) {
02876                                                         $this->pagedim[$this->page]['trans']['Di'] = intval($format['trans']['Di']);
02877                                                 }
02878                                         }
02879                                         if (isset($format['trans']['SS']) AND ($format['trans']['S'] == 'Fly')) {
02880                                                 $this->pagedim[$this->page]['trans']['SS'] = floatval($format['trans']['SS']);
02881                                         }
02882                                         if (isset($format['trans']['B']) AND ($format['trans']['B'] === true) AND ($format['trans']['S'] == 'Fly')) {
02883                                                 $this->pagedim[$this->page]['trans']['B'] = 'true';
02884                                         }
02885                                 } else {
02886                                         $this->pagedim[$this->page]['trans']['S'] = 'R';
02887                                 }
02888                                 if (isset($format['trans']['D'])) {
02889                                         // The duration of the transition effect, in seconds
02890                                         $this->pagedim[$this->page]['trans']['D'] = floatval($format['trans']['D']);
02891                                 } else {
02892                                         $this->pagedim[$this->page]['trans']['D'] = 1;
02893                                 }
02894                         }
02895                 }
02896                 $this->setPageOrientation($orientation);
02897         }
02898 
02911         public function setPageBoxes($page, $type, $llx, $lly, $urx, $ury, $points=false) {
02912                 if (!isset($this->pagedim[$page])) {
02913                         // initialize array
02914                         $this->pagedim[$page] = array();
02915                 }
02916                 $pageboxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox');
02917                 if (!in_array($type, $pageboxes)) {
02918                         return;
02919                 }
02920                 if ($points) {
02921                         $k = 1;
02922                 } else {
02923                         $k = $this->k;
02924                 }
02925                 $this->pagedim[$page][$type]['llx'] = ($llx * $k);
02926                 $this->pagedim[$page][$type]['lly'] = ($lly * $k);
02927                 $this->pagedim[$page][$type]['urx'] = ($urx * $k);
02928                 $this->pagedim[$page][$type]['ury'] = ($ury * $k);
02929         }
02930 
02937         protected function swapPageBoxCoordinates($page) {
02938                 $pageboxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox');
02939                 foreach ($pageboxes as $type) {
02940                         // swap X and Y coordinates
02941                         if (isset($this->pagedim[$page][$type])) {
02942                                 $tmp = $this->pagedim[$page][$type]['llx'];
02943                                 $this->pagedim[$page][$type]['llx'] = $this->pagedim[$page][$type]['lly'];
02944                                 $this->pagedim[$page][$type]['lly'] = $tmp;
02945                                 $tmp = $this->pagedim[$page][$type]['urx'];
02946                                 $this->pagedim[$page][$type]['urx'] = $this->pagedim[$page][$type]['ury'];
02947                                 $this->pagedim[$page][$type]['ury'] = $tmp;
02948                         }
02949                 }
02950         }
02951 
02960         public function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') {
02961                 if (!isset($this->pagedim[$this->page]['MediaBox'])) {
02962                         // the boundaries of the physical medium on which the page shall be displayed or printed
02963                         $this->setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true);
02964                 }
02965                 if (!isset($this->pagedim[$this->page]['CropBox'])) {
02966                         // the visible region of default user space
02967                         $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);
02968                 }
02969                 if (!isset($this->pagedim[$this->page]['BleedBox'])) {
02970                         // the region to which the contents of the page shall be clipped when output in a production environment
02971                         $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);
02972                 }
02973                 if (!isset($this->pagedim[$this->page]['TrimBox'])) {
02974                         // the intended dimensions of the finished page after trimming
02975                         $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);
02976                 }
02977                 if (!isset($this->pagedim[$this->page]['ArtBox'])) {
02978                         // the page's meaningful content (including potential white space)
02979                         $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);
02980                 }
02981                 if (!isset($this->pagedim[$this->page]['Rotate'])) {
02982                         // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.
02983                         $this->pagedim[$this->page]['Rotate'] = 0;
02984                 }
02985                 if (!isset($this->pagedim[$this->page]['PZ'])) {
02986                         // The page's preferred zoom (magnification) factor
02987                         $this->pagedim[$this->page]['PZ'] = 1;
02988                 }
02989                 if ($this->fwPt > $this->fhPt) {
02990                         // landscape
02991                         $default_orientation = 'L';
02992                 } else {
02993                         // portrait
02994                         $default_orientation = 'P';
02995                 }
02996                 $valid_orientations = array('P', 'L');
02997                 if (empty($orientation)) {
02998                         $orientation = $default_orientation;
02999                 } else {
03000                         $orientation = strtoupper($orientation{0});
03001                 }
03002                 if (in_array($orientation, $valid_orientations) AND ($orientation != $default_orientation)) {
03003                         $this->CurOrientation = $orientation;
03004                         $this->wPt = $this->fhPt;
03005                         $this->hPt = $this->fwPt;
03006                 } else {
03007                         $this->CurOrientation = $default_orientation;
03008                         $this->wPt = $this->fwPt;
03009                         $this->hPt = $this->fhPt;
03010                 }
03011                 if ((abs($this->pagedim[$this->page]['MediaBox']['urx'] - $this->hPt) < $this->feps) AND (abs($this->pagedim[$this->page]['MediaBox']['ury'] - $this->wPt) < $this->feps)){
03012                         // swap X and Y coordinates (change page orientation)
03013                         $this->swapPageBoxCoordinates($this->page);
03014                 }
03015                 $this->w = $this->wPt / $this->k;
03016                 $this->h = $this->hPt / $this->k;
03017                 if ($this->empty_string($autopagebreak)) {
03018                         if (isset($this->AutoPageBreak)) {
03019                                 $autopagebreak = $this->AutoPageBreak;
03020                         } else {
03021                                 $autopagebreak = true;
03022                         }
03023                 }
03024                 if ($this->empty_string($bottommargin)) {
03025                         if (isset($this->bMargin)) {
03026                                 $bottommargin = $this->bMargin;
03027                         } else {
03028                                 // default value = 2 cm
03029                                 $bottommargin = 2 * 28.35 / $this->k;
03030                         }
03031                 }
03032                 $this->SetAutoPageBreak($autopagebreak, $bottommargin);
03033                 // store page dimensions
03034                 $this->pagedim[$this->page]['w'] = $this->wPt;
03035                 $this->pagedim[$this->page]['h'] = $this->hPt;
03036                 $this->pagedim[$this->page]['wk'] = $this->w;
03037                 $this->pagedim[$this->page]['hk'] = $this->h;
03038                 $this->pagedim[$this->page]['tm'] = $this->tMargin;
03039                 $this->pagedim[$this->page]['bm'] = $bottommargin;
03040                 $this->pagedim[$this->page]['lm'] = $this->lMargin;
03041                 $this->pagedim[$this->page]['rm'] = $this->rMargin;
03042                 $this->pagedim[$this->page]['pb'] = $autopagebreak;
03043                 $this->pagedim[$this->page]['or'] = $this->CurOrientation;
03044                 $this->pagedim[$this->page]['olm'] = $this->original_lMargin;
03045                 $this->pagedim[$this->page]['orm'] = $this->original_rMargin;
03046         }
03047 
03065         public function setSpacesRE($re='/[^\S\xa0]/') {
03066                 $this->re_spaces = $re;
03067                 $re_parts = explode('/', $re);
03068                 // get pattern parts
03069                 $this->re_space = array();
03070                 if (isset($re_parts[1]) AND !empty($re_parts[1])) {
03071                         $this->re_space['p'] = $re_parts[1];
03072                 } else {
03073                         $this->re_space['p'] = '[\s]';
03074                 }
03075                 // set pattern modifiers
03076                 if (isset($re_parts[2]) AND !empty($re_parts[2])) {
03077                         $this->re_space['m'] = $re_parts[2];
03078                 } else {
03079                         $this->re_space['m'] = '';
03080                 }
03081         }
03082 
03090         public function setRTL($enable, $resetx=true) {
03091                 $enable = $enable ? true : false;
03092                 $resetx = ($resetx AND ($enable != $this->rtl));
03093                 $this->rtl = $enable;
03094                 $this->tmprtl = false;
03095                 if ($resetx) {
03096                         $this->Ln(0);
03097                 }
03098         }
03099 
03106         public function getRTL() {
03107                 return $this->rtl;
03108         }
03109 
03116         public function setTempRTL($mode) {
03117                 $newmode = false;
03118                 switch (strtoupper($mode)) {
03119                         case 'LTR':
03120                         case 'L': {
03121                                 if ($this->rtl) {
03122                                         $newmode = 'L';
03123                                 }
03124                                 break;
03125                         }
03126                         case 'RTL':
03127                         case 'R': {
03128                                 if (!$this->rtl) {
03129                                         $newmode = 'R';
03130                                 }
03131                                 break;
03132                         }
03133                         case false:
03134                         default: {
03135                                 $newmode = false;
03136                                 break;
03137                         }
03138                 }
03139                 $this->tmprtl = $newmode;
03140         }
03141 
03148         public function isRTLTextDir() {
03149                 return ($this->rtl OR ($this->tmprtl == 'R'));
03150         }
03151 
03159         public function setLastH($h) {
03160                 $this->lasth = $h;
03161         }
03162 
03168         public function resetLastH() {
03169                 $this->lasth = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
03170         }
03171 
03178         public function getLastH() {
03179                 return $this->lasth;
03180         }
03181 
03189         public function setImageScale($scale) {
03190                 $this->imgscale = $scale;
03191         }
03192 
03200         public function getImageScale() {
03201                 return $this->imgscale;
03202         }
03203 
03213         public function getPageDimensions($pagenum='') {
03214                 if (empty($pagenum)) {
03215                         $pagenum = $this->page;
03216                 }
03217                 return $this->pagedim[$pagenum];
03218         }
03219 
03229         public function getPageWidth($pagenum='') {
03230                 if (empty($pagenum)) {
03231                         return $this->w;
03232                 }
03233                 return $this->pagedim[$pagenum]['w'];
03234         }
03235 
03245         public function getPageHeight($pagenum='') {
03246                 if (empty($pagenum)) {
03247                         return $this->h;
03248                 }
03249                 return $this->pagedim[$pagenum]['h'];
03250         }
03251 
03261         public function getBreakMargin($pagenum='') {
03262                 if (empty($pagenum)) {
03263                         return $this->bMargin;
03264                 }
03265                 return $this->pagedim[$pagenum]['bm'];
03266         }
03267 
03275         public function getScaleFactor() {
03276                 return $this->k;
03277         }
03278 
03289         public function SetMargins($left, $top, $right=-1, $keepmargins=false) {
03290                 //Set left, top and right margins
03291                 $this->lMargin = $left;
03292                 $this->tMargin = $top;
03293                 if ($right == -1) {
03294                         $right = $left;
03295                 }
03296                 $this->rMargin = $right;
03297                 if ($keepmargins) {
03298                         // overwrite original values
03299                         $this->original_lMargin = $this->lMargin;
03300                         $this->original_rMargin = $this->rMargin;
03301                 }
03302         }
03303 
03311         public function SetLeftMargin($margin) {
03312                 //Set left margin
03313                 $this->lMargin = $margin;
03314                 if (($this->page > 0) AND ($this->x < $margin)) {
03315                         $this->x = $margin;
03316                 }
03317         }
03318 
03326         public function SetTopMargin($margin) {
03327                 //Set top margin
03328                 $this->tMargin = $margin;
03329                 if (($this->page > 0) AND ($this->y < $margin)) {
03330                         $this->y = $margin;
03331                 }
03332         }
03333 
03341         public function SetRightMargin($margin) {
03342                 $this->rMargin = $margin;
03343                 if (($this->page > 0) AND ($this->x > ($this->w - $margin))) {
03344                         $this->x = $this->w - $margin;
03345                 }
03346         }
03347 
03355         public function SetCellPadding($pad) {
03356                 if ($pad >= 0) {
03357                         $this->cell_padding['L'] = $pad;
03358                         $this->cell_padding['T'] = $pad;
03359                         $this->cell_padding['R'] = $pad;
03360                         $this->cell_padding['B'] = $pad;
03361                 }
03362         }
03363 
03374         public function setCellPaddings($left='', $top='', $right='', $bottom='') {
03375                 if (($left !== '') AND ($left >= 0)) {
03376                         $this->cell_padding['L'] = $left;
03377                 }
03378                 if (($top !== '') AND ($top >= 0)) {
03379                         $this->cell_padding['T'] = $top;
03380                 }
03381                 if (($right !== '') AND ($right >= 0)) {
03382                         $this->cell_padding['R'] = $right;
03383                 }
03384                 if (($bottom !== '') AND ($bottom >= 0)) {
03385                         $this->cell_padding['B'] = $bottom;
03386                 }
03387         }
03388 
03396         public function getCellPaddings() {
03397                 return $this->cell_padding;
03398         }
03399 
03410         public function setCellMargins($left='', $top='', $right='', $bottom='') {
03411                 if (($left !== '') AND ($left >= 0)) {
03412                         $this->cell_margin['L'] = $left;
03413                 }
03414                 if (($top !== '') AND ($top >= 0)) {
03415                         $this->cell_margin['T'] = $top;
03416                 }
03417                 if (($right !== '') AND ($right >= 0)) {
03418                         $this->cell_margin['R'] = $right;
03419                 }
03420                 if (($bottom !== '') AND ($bottom >= 0)) {
03421                         $this->cell_margin['B'] = $bottom;
03422                 }
03423         }
03424 
03432         public function getCellMargins() {
03433                 return $this->cell_margin;
03434         }
03435 
03443         protected function adjustCellPadding($brd=0) {
03444                 if (empty($brd)) {
03445                         return;
03446                 }
03447                 if (is_string($brd)) {
03448                         // convert string to array
03449                         $slen = strlen($brd);
03450                         $newbrd = array();
03451                         for ($i = 0; $i < $slen; ++$i) {
03452                                 $newbrd[$brd{$i}] = true;
03453                         }
03454                         $brd = $newbrd;
03455                 } elseif (($brd === 1) OR ($brd === true) OR (is_numeric($brd) AND (intval($brd) > 0))) {
03456                         $brd = array('LRTB' => true);
03457                 }
03458                 if (!is_array($brd)) {
03459                         return;
03460                 }
03461                 // store current cell padding
03462                 $cp = $this->cell_padding;
03463                 // select border mode
03464                 if (isset($brd['mode'])) {
03465                         $mode = $brd['mode'];
03466                         unset($brd['mode']);
03467                 } else {
03468                         $mode = 'normal';
03469                 }
03470                 // process borders
03471                 foreach ($brd as $border => $style) {
03472                         $line_width = $this->LineWidth;
03473                         if (is_array($style) AND isset($style['width'])) {
03474                                 // get border width
03475                                 $line_width = $style['width'];
03476                         }
03477                         $adj = 0; // line width inside the cell
03478                         switch ($mode) {
03479                                 case 'ext': {
03480                                         $adj = 0;
03481                                         break;
03482                                 }
03483                                 case 'int': {
03484                                         $adj = $line_width;
03485                                         break;
03486                                 }
03487                                 case 'normal':
03488                                 default: {
03489                                         $adj = ($line_width / 2);
03490                                         break;
03491                                 }
03492                         }
03493                         // correct internal cell padding if required to avoid overlap between text and lines
03494                         if ((strpos($border,'T') !== false) AND ($this->cell_padding['T'] < $adj)) {
03495                                 $this->cell_padding['T'] = $adj;
03496                         }
03497                         if ((strpos($border,'R') !== false) AND ($this->cell_padding['R'] < $adj)) {
03498                                 $this->cell_padding['R'] = $adj;
03499                         }
03500                         if ((strpos($border,'B') !== false) AND ($this->cell_padding['B'] < $adj)) {
03501                                 $this->cell_padding['B'] = $adj;
03502                         }
03503                         if ((strpos($border,'L') !== false) AND ($this->cell_padding['L'] < $adj)) {
03504                                 $this->cell_padding['L'] = $adj;
03505                         }
03506                 }
03507                 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']));
03508         }
03509 
03518         public function SetAutoPageBreak($auto, $margin=0) {
03519                 $this->AutoPageBreak = $auto ? true : false;
03520                 $this->bMargin = $margin;
03521                 $this->PageBreakTrigger = $this->h - $margin;
03522         }
03523 
03530         public function getAutoPageBreak() {
03531                 return $this->AutoPageBreak;
03532         }
03533 
03542         public function SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone') {
03543                 if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) {
03544                         $this->ZoomMode = $zoom;
03545                 } else {
03546                         $this->Error('Incorrect zoom display mode: '.$zoom);
03547                 }
03548                 switch ($layout) {
03549                         case 'default':
03550                         case 'single':
03551                         case 'SinglePage': {
03552                                 $this->LayoutMode = 'SinglePage';
03553                                 break;
03554                         }
03555                         case 'continuous':
03556                         case 'OneColumn': {
03557                                 $this->LayoutMode = 'OneColumn';
03558                                 break;
03559                         }
03560                         case 'two':
03561                         case 'TwoColumnLeft': {
03562                                 $this->LayoutMode = 'TwoColumnLeft';
03563                                 break;
03564                         }
03565                         case 'TwoColumnRight': {
03566                                 $this->LayoutMode = 'TwoColumnRight';
03567                                 break;
03568                         }
03569                         case 'TwoPageLeft': {
03570                                 $this->LayoutMode = 'TwoPageLeft';
03571                                 break;
03572                         }
03573                         case 'TwoPageRight': {
03574                                 $this->LayoutMode = 'TwoPageRight';
03575                                 break;
03576                         }
03577                         default: {
03578                                 $this->LayoutMode = 'SinglePage';
03579                         }
03580                 }
03581                 switch ($mode) {
03582                         case 'UseNone': {
03583                                 $this->PageMode = 'UseNone';
03584                                 break;
03585                         }
03586                         case 'UseOutlines': {
03587                                 $this->PageMode = 'UseOutlines';
03588                                 break;
03589                         }
03590                         case 'UseThumbs': {
03591                                 $this->PageMode = 'UseThumbs';
03592                                 break;
03593                         }
03594                         case 'FullScreen': {
03595                                 $this->PageMode = 'FullScreen';
03596                                 break;
03597                         }
03598                         case 'UseOC': {
03599                                 $this->PageMode = 'UseOC';
03600                                 break;
03601                         }
03602                         case '': {
03603                                 $this->PageMode = 'UseAttachments';
03604                                 break;
03605                         }
03606                         default: {
03607                                 $this->PageMode = 'UseNone';
03608                         }
03609                 }
03610         }
03611 
03619         public function SetCompression($compress=true) {
03620                 if (function_exists('gzcompress')) {
03621                         $this->compress = $compress ? true : false;
03622                 } else {
03623                         $this->compress = false;
03624                 }
03625         }
03626 
03633         public function setSRGBmode($mode=false) {
03634                 $this->force_srgb = $mode ? true : false;
03635         }
03636 
03644         public function SetDocInfoUnicode($unicode=true) {
03645                 $this->docinfounicode = $unicode ? true : false;
03646         }
03647 
03655         public function SetTitle($title) {
03656                 $this->title = $title;
03657         }
03658 
03666         public function SetSubject($subject) {
03667                 $this->subject = $subject;
03668         }
03669 
03677         public function SetAuthor($author) {
03678                 $this->author = $author;
03679         }
03680 
03688         public function SetKeywords($keywords) {
03689                 $this->keywords = $keywords;
03690         }
03691 
03699         public function SetCreator($creator) {
03700                 $this->creator = $creator;
03701         }
03702 
03710         public function Error($msg) {
03711                 // unset all class variables
03712                 $this->_destroy(true);
03713                 // exit program and print error
03714                 die('<strong>TCPDF ERROR: </strong>'.$msg);
03715         }
03716 
03725         public function Open() {
03726                 $this->state = 1;
03727         }
03728 
03737         public function Close() {
03738                 if ($this->state == 3) {
03739                         return;
03740                 }
03741                 if ($this->page == 0) {
03742                         $this->AddPage();
03743                 }
03744                 $this->endLayer();
03745                 // save current graphic settings
03746                 $gvars = $this->getGraphicVars();
03747                 $this->setEqualColumns();
03748                 $this->lastpage(true);
03749                 $this->SetAutoPageBreak(false);
03750                 $this->x = 0;
03751                 $this->y = $this->h - (1 / $this->k);
03752                 $this->lMargin = 0;
03753                 $this->_out('q');
03754                 $this->SetFont('helvetica', '', 1);
03755                 $this->setTextRenderingMode(0, false, false);
03756                 $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";
03757                 $lnk = "\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67";
03758                 $this->Cell(0, 0, $msg, 0, 0, 'L', 0, $lnk, 0, false, 'D', 'B');
03759                 $this->_out('Q');
03760                 // restore graphic settings
03761                 $this->setGraphicVars($gvars);
03762                 // close page
03763                 $this->endPage();
03764                 // close document
03765                 $this->_enddoc();
03766                 // unset all class variables (except critical ones)
03767                 $this->_destroy(false);
03768         }
03769 
03778         public function setPage($pnum, $resetmargins=false) {
03779                 if (($pnum == $this->page) AND ($this->state == 2)) {
03780                         return;
03781                 }
03782                 if (($pnum > 0) AND ($pnum <= $this->numpages)) {
03783                         $this->state = 2;
03784                         // save current graphic settings
03785                         //$gvars = $this->getGraphicVars();
03786                         $oldpage = $this->page;
03787                         $this->page = $pnum;
03788                         $this->wPt = $this->pagedim[$this->page]['w'];
03789                         $this->hPt = $this->pagedim[$this->page]['h'];
03790                         $this->w = $this->pagedim[$this->page]['wk'];
03791                         $this->h = $this->pagedim[$this->page]['hk'];
03792                         $this->tMargin = $this->pagedim[$this->page]['tm'];
03793                         $this->bMargin = $this->pagedim[$this->page]['bm'];
03794                         $this->original_lMargin = $this->pagedim[$this->page]['olm'];
03795                         $this->original_rMargin = $this->pagedim[$this->page]['orm'];
03796                         $this->AutoPageBreak = $this->pagedim[$this->page]['pb'];
03797                         $this->CurOrientation = $this->pagedim[$this->page]['or'];
03798                         $this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin);
03799                         // restore graphic settings
03800                         //$this->setGraphicVars($gvars);
03801                         if ($resetmargins) {
03802                                 $this->lMargin = $this->pagedim[$this->page]['olm'];
03803                                 $this->rMargin = $this->pagedim[$this->page]['orm'];
03804                                 $this->SetY($this->tMargin);
03805                         } else {
03806                                 // account for booklet mode
03807                                 if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
03808                                         $deltam = $this->pagedim[$this->page]['olm'] - $this->pagedim[$this->page]['orm'];
03809                                         $this->lMargin += $deltam;
03810                                         $this->rMargin -= $deltam;
03811                                 }
03812                         }
03813                 } else {
03814                         $this->Error('Wrong page number on setPage() function: '.$pnum);
03815                 }
03816         }
03817 
03825         public function lastPage($resetmargins=false) {
03826                 $this->setPage($this->getNumPages(), $resetmargins);
03827         }
03828 
03836         public function getPage() {
03837                 return $this->page;
03838         }
03839 
03847         public function getNumPages() {
03848                 return $this->numpages;
03849         }
03850 
03860         public function addTOCPage($orientation='', $format='', $keepmargins=false) {
03861                 $this->AddPage($orientation, $format, $keepmargins, true);
03862         }
03863 
03870         public function endTOCPage() {
03871                 $this->endPage(true);
03872         }
03873 
03885         public function AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false) {
03886                 if ($this->inxobj) {
03887                         // we are inside an XObject template
03888                         return;
03889                 }
03890                 if (!isset($this->original_lMargin) OR $keepmargins) {
03891                         $this->original_lMargin = $this->lMargin;
03892                 }
03893                 if (!isset($this->original_rMargin) OR $keepmargins) {
03894                         $this->original_rMargin = $this->rMargin;
03895                 }
03896                 // terminate previous page
03897                 $this->endPage();
03898                 // start new page
03899                 $this->startPage($orientation, $format, $tocpage);
03900         }
03901 
03909         public function endPage($tocpage=false) {
03910                 // check if page is already closed
03911                 if (($this->page == 0) OR ($this->numpages > $this->page) OR (!$this->pageopen[$this->page])) {
03912                         return;
03913                 }
03914                 // print page footer
03915                 $this->setFooter();
03916                 // close page
03917                 $this->_endpage();
03918                 // mark page as closed
03919                 $this->pageopen[$this->page] = false;
03920                 if ($tocpage) {
03921                         $this->tocpage = false;
03922                 }
03923         }
03924 
03935         public function startPage($orientation='', $format='', $tocpage=false) {
03936                 if ($tocpage) {
03937                         $this->tocpage = true;
03938                 }
03939                 // move page numbers of documents to be attached
03940                 if ($this->tocpage) {
03941                         // move reference to unexistent pages (used for page attachments)
03942                         // adjust outlines
03943                         $tmpoutlines = $this->outlines;
03944                         foreach ($tmpoutlines as $key => $outline) {
03945                                 if ($outline['p'] > $this->numpages) {
03946                                         $this->outlines[$key]['p'] = ($outline['p'] + 1);
03947                                 }
03948                         }
03949                         // adjust dests
03950                         $tmpdests = $this->dests;
03951                         foreach ($tmpdests as $key => $dest) {
03952                                 if ($dest['p'] > $this->numpages) {
03953                                         $this->dests[$key]['p'] = ($dest['p'] + 1);
03954                                 }
03955                         }
03956                         // adjust links
03957                         $tmplinks = $this->links;
03958                         foreach ($tmplinks as $key => $link) {
03959                                 if ($link[0] > $this->numpages) {
03960                                         $this->links[$key][0] = ($link[0] + 1);
03961                                 }
03962                         }
03963                 }
03964                 if ($this->numpages > $this->page) {
03965                         // this page has been already added
03966                         $this->setPage($this->page + 1);
03967                         $this->SetY($this->tMargin);
03968                         return;
03969                 }
03970                 // start a new page
03971                 if ($this->state == 0) {
03972                         $this->Open();
03973                 }
03974                 ++$this->numpages;
03975                 $this->swapMargins($this->booklet);
03976                 // save current graphic settings
03977                 $gvars = $this->getGraphicVars();
03978                 // start new page
03979                 $this->_beginpage($orientation, $format);
03980                 // mark page as open
03981                 $this->pageopen[$this->page] = true;
03982                 // restore graphic settings
03983                 $this->setGraphicVars($gvars);
03984                 // mark this point
03985                 $this->setPageMark();
03986                 // print page header
03987                 $this->setHeader();
03988                 // restore graphic settings
03989                 $this->setGraphicVars($gvars);
03990                 // mark this point
03991                 $this->setPageMark();
03992                 // print table header (if any)
03993                 $this->setTableHeader();
03994                 // set mark for empty page check
03995                 $this->emptypagemrk[$this->page]= $this->pagelen[$this->page];
03996         }
03997 
04006         public function setPageMark() {
04007                 $this->intmrk[$this->page] = $this->pagelen[$this->page];
04008                 $this->bordermrk[$this->page] = $this->intmrk[$this->page];
04009                 $this->setContentMark();
04010         }
04011 
04019         protected function setContentMark($page=0) {
04020                 if ($page <= 0) {
04021                         $page = $this->page;
04022                 }
04023                 if (isset($this->footerlen[$page])) {
04024                         $this->cntmrk[$page] = $this->pagelen[$page] - $this->footerlen[$page];
04025                 } else {
04026                         $this->cntmrk[$page] = $this->pagelen[$page];
04027                 }
04028         }
04029 
04038         public function setHeaderData($ln='', $lw=0, $ht='', $hs='') {
04039                 $this->header_logo = $ln;
04040                 $this->header_logo_width = $lw;
04041                 $this->header_title = $ht;
04042                 $this->header_string = $hs;
04043         }
04044 
04052         public function getHeaderData() {
04053                 $ret = array();
04054                 $ret['logo'] = $this->header_logo;
04055                 $ret['logo_width'] = $this->header_logo_width;
04056                 $ret['title'] = $this->header_title;
04057                 $ret['string'] = $this->header_string;
04058                 return $ret;
04059         }
04060 
04067         public function setHeaderMargin($hm=10) {
04068                 $this->header_margin = $hm;
04069         }
04070 
04077         public function getHeaderMargin() {
04078                 return $this->header_margin;
04079         }
04080 
04087         public function setFooterMargin($fm=10) {
04088                 $this->footer_margin = $fm;
04089         }
04090 
04097         public function getFooterMargin() {
04098                 return $this->footer_margin;
04099         }
04105         public function setPrintHeader($val=true) {
04106                 $this->print_header = $val ? true : false;
04107         }
04108 
04114         public function setPrintFooter($val=true) {
04115                 $this->print_footer = $val ? true : false;
04116         }
04117 
04123         public function getImageRBX() {
04124                 return $this->img_rb_x;
04125         }
04126 
04132         public function getImageRBY() {
04133                 return $this->img_rb_y;
04134         }
04135 
04140         public function resetHeaderTemplate() {
04141                 $this->header_xobjid = -1;
04142         }
04143 
04149         public function setHeaderTemplateAutoreset($val=true) {
04150                 $this->header_xobj_autoreset = $val ? true : false;
04151         }
04152 
04158         public function Header() {
04159                 if ($this->header_xobjid < 0) {
04160                         // start a new XObject Template
04161                         $this->header_xobjid = $this->startTemplate($this->w, $this->tMargin);
04162                         $headerfont = $this->getHeaderFont();
04163                         $headerdata = $this->getHeaderData();
04164                         $this->y = $this->header_margin;
04165                         if ($this->rtl) {
04166                                 $this->x = $this->w - $this->original_rMargin;
04167                         } else {
04168                                 $this->x = $this->original_lMargin;
04169                         }
04170                         if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) {
04171                                 $imgtype = $this->getImageFileType(K_PATH_IMAGES.$headerdata['logo']);
04172                                 if (($imgtype == 'eps') OR ($imgtype == 'ai')) {
04173                                         $this->ImageEps(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
04174                                 } elseif ($imgtype == 'svg') {
04175                                         $this->ImageSVG(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
04176                                 } else {
04177                                         $this->Image(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
04178                                 }
04179                                 $imgy = $this->getImageRBY();
04180                         } else {
04181                                 $imgy = $this->y;
04182                         }
04183                         $cell_height = round(($this->cell_height_ratio * $headerfont[2]) / $this->k, 2);
04184                         // set starting margin for text data cell
04185                         if ($this->getRTL()) {
04186                                 $header_x = $this->original_rMargin + ($headerdata['logo_width'] * 1.1);
04187                         } else {
04188                                 $header_x = $this->original_lMargin + ($headerdata['logo_width'] * 1.1);
04189                         }
04190                         $cw = $this->w - $this->original_lMargin - $this->original_rMargin - ($headerdata['logo_width'] * 1.1);
04191                         $this->SetTextColor(0, 0, 0);
04192                         // header title
04193                         $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1);
04194                         $this->SetX($header_x);
04195                         $this->Cell($cw, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0);
04196                         // header string
04197                         $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]);
04198                         $this->SetX($header_x);
04199                         $this->MultiCell($cw, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false, true, 0, 'T', false);
04200                         // print an ending header line
04201                         $this->SetLineStyle(array('width' => 0.85 / $this->k, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)));
04202                         $this->SetY((2.835 / $this->k) + max($imgy, $this->y));
04203                         if ($this->rtl) {
04204                                 $this->SetX($this->original_rMargin);
04205                         } else {
04206                                 $this->SetX($this->original_lMargin);
04207                         }
04208                         $this->Cell(($this->w - $this->original_lMargin - $this->original_rMargin), 0, '', 'T', 0, 'C');
04209                         $this->endTemplate();
04210                 }
04211                 // print header template
04212                 $x = 0;
04213                 $dx = 0;
04214                 if ($this->booklet AND (($this->page % 2) == 0)) {
04215                         // adjust margins for booklet mode
04216                         $dx = ($this->original_lMargin - $this->original_rMargin);
04217                 }
04218                 if ($this->rtl) {
04219                         $x = $this->w + $dx;
04220                 } else {
04221                         $x = 0 + $dx;
04222                 }
04223                 $this->printTemplate($this->header_xobjid, $x, 0, 0, 0, '', '', false);
04224                 if ($this->header_xobj_autoreset) {
04225                         // reset header xobject template at each page
04226                         $this->header_xobjid = -1;
04227                 }
04228         }
04229 
04235         public function Footer() {
04236                 $cur_y = $this->y;
04237                 $this->SetTextColor(0, 0, 0);
04238                 //set style for cell border
04239                 $line_width = 0.85 / $this->k;
04240                 $this->SetLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)));
04241                 //print document barcode
04242                 $barcode = $this->getBarcode();
04243                 if (!empty($barcode)) {
04244                         $this->Ln($line_width);
04245                         $barcode_width = round(($this->w - $this->original_lMargin - $this->original_rMargin) / 3);
04246                         $style = array(
04247                                 'position' => $this->rtl?'R':'L',
04248                                 'align' => $this->rtl?'R':'L',
04249                                 'stretch' => false,
04250                                 'fitwidth' => true,
04251                                 'cellfitalign' => '',
04252                                 'border' => false,
04253                                 'padding' => 0,
04254                                 'fgcolor' => array(0,0,0),
04255                                 'bgcolor' => false,
04256                                 'text' => false
04257                         );
04258                         $this->write1DBarcode($barcode, 'C128', '', $cur_y + $line_width, '', (($this->footer_margin / 3) - $line_width), 0.3, $style, '');
04259                 }
04260                 if (empty($this->pagegroups)) {
04261                         $pagenumtxt = $this->l['w_page'].' '.$this->getAliasNumPage().' / '.$this->getAliasNbPages();
04262                 } else {
04263                         $pagenumtxt = $this->l['w_page'].' '.$this->getPageNumGroupAlias().' / '.$this->getPageGroupAlias();
04264                 }
04265                 $this->SetY($cur_y);
04266                 //Print page number
04267                 if ($this->getRTL()) {
04268                         $this->SetX($this->original_rMargin);
04269                         $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L');
04270                 } else {
04271                         $this->SetX($this->original_lMargin);
04272                         $this->Cell(0, 0, $this->getAliasRightShift().$pagenumtxt, 'T', 0, 'R');
04273                 }
04274         }
04275 
04281         protected function setHeader() {
04282                 if (!$this->print_header) {
04283                         return;
04284                 }
04285                 $this->InHeader = true;
04286                 $this->setGraphicVars($this->default_graphic_vars);
04287                 $temp_thead = $this->thead;
04288                 $temp_theadMargins = $this->theadMargins;
04289                 $lasth = $this->lasth;
04290                 $this->_out('q');
04291                 $this->rMargin = $this->original_rMargin;
04292                 $this->lMargin = $this->original_lMargin;
04293                 $this->SetCellPadding(0);
04294                 //set current position
04295                 if ($this->rtl) {
04296                         $this->SetXY($this->original_rMargin, $this->header_margin);
04297                 } else {
04298                         $this->SetXY($this->original_lMargin, $this->header_margin);
04299                 }
04300                 $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]);
04301                 $this->Header();
04302                 //restore position
04303                 if ($this->rtl) {
04304                         $this->SetXY($this->original_rMargin, $this->tMargin);
04305                 } else {
04306                         $this->SetXY($this->original_lMargin, $this->tMargin);
04307                 }
04308                 $this->_out('Q');
04309                 $this->lasth = $lasth;
04310                 $this->thead = $temp_thead;
04311                 $this->theadMargins = $temp_theadMargins;
04312                 $this->newline = false;
04313                 $this->InHeader = false;
04314         }
04315 
04321         protected function setFooter() {
04322                 //Page footer
04323                 $this->InFooter = true;
04324                 // save current graphic settings
04325                 $gvars = $this->getGraphicVars();
04326                 // mark this point
04327                 $this->footerpos[$this->page] = $this->pagelen[$this->page];
04328                 $this->_out("\n");
04329                 if ($this->print_footer) {
04330                         $this->setGraphicVars($this->default_graphic_vars);
04331                         $this->current_column = 0;
04332                         $this->num_columns = 1;
04333                         $temp_thead = $this->thead;
04334                         $temp_theadMargins = $this->theadMargins;
04335                         $lasth = $this->lasth;
04336                         $this->_out('q');
04337                         $this->rMargin = $this->original_rMargin;
04338                         $this->lMargin = $this->original_lMargin;
04339                         $this->SetCellPadding(0);
04340                         //set current position
04341                         $footer_y = $this->h - $this->footer_margin;
04342                         if ($this->rtl) {
04343                                 $this->SetXY($this->original_rMargin, $footer_y);
04344                         } else {
04345                                 $this->SetXY($this->original_lMargin, $footer_y);
04346                         }
04347                         $this->SetFont($this->footer_font[0], $this->footer_font[1], $this->footer_font[2]);
04348                         $this->Footer();
04349                         //restore position
04350                         if ($this->rtl) {
04351                                 $this->SetXY($this->original_rMargin, $this->tMargin);
04352                         } else {
04353                                 $this->SetXY($this->original_lMargin, $this->tMargin);
04354                         }
04355                         $this->_out('Q');
04356                         $this->lasth = $lasth;
04357                         $this->thead = $temp_thead;
04358                         $this->theadMargins = $temp_theadMargins;
04359                 }
04360                 // restore graphic settings
04361                 $this->setGraphicVars($gvars);
04362                 $this->current_column = $gvars['current_column'];
04363                 $this->num_columns = $gvars['num_columns'];
04364                 // calculate footer length
04365                 $this->footerlen[$this->page] = $this->pagelen[$this->page] - $this->footerpos[$this->page] + 1;
04366                 $this->InFooter = false;
04367         }
04368 
04375         protected function inPageBody() {
04376                 return (($this->InHeader === false) AND ($this->InFooter === false));
04377         }
04378 
04384         protected function setTableHeader() {
04385                 if ($this->num_columns > 1) {
04386                         // multi column mode
04387                         return;
04388                 }
04389                 if (isset($this->theadMargins['top'])) {
04390                         // restore the original top-margin
04391                         $this->tMargin = $this->theadMargins['top'];
04392                         $this->pagedim[$this->page]['tm'] = $this->tMargin;
04393                         $this->y = $this->tMargin;
04394                 }
04395                 if (!$this->empty_string($this->thead) AND (!$this->inthead)) {
04396                         // set margins
04397                         $prev_lMargin = $this->lMargin;
04398                         $prev_rMargin = $this->rMargin;
04399                         $prev_cell_padding = $this->cell_padding;
04400                         $this->lMargin = $this->theadMargins['lmargin'] + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$this->theadMargins['page']]['olm']);
04401                         $this->rMargin = $this->theadMargins['rmargin'] + ($this->pagedim[$this->page]['orm'] - $this->pagedim[$this->theadMargins['page']]['orm']);
04402                         $this->cell_padding = $this->theadMargins['cell_padding'];
04403                         if ($this->rtl) {
04404                                 $this->x = $this->w - $this->rMargin;
04405                         } else {
04406                                 $this->x = $this->lMargin;
04407                         }
04408                         // account for special "cell" mode
04409                         if ($this->theadMargins['cell']) {
04410                                 if ($this->rtl) {
04411                                         $this->x -= $this->cell_padding['R'];
04412                                 } else {
04413                                         $this->x += $this->cell_padding['L'];
04414                                 }
04415                         }
04416                         // print table header
04417                         $this->writeHTML($this->thead, false, false, false, false, '');
04418                         // set new top margin to skip the table headers
04419                         if (!isset($this->theadMargins['top'])) {
04420                                 $this->theadMargins['top'] = $this->tMargin;
04421                         }
04422                         // store end of header position
04423                         if (!isset($this->columns[0]['th'])) {
04424                                 $this->columns[0]['th'] = array();
04425                         }
04426                         $this->columns[0]['th']['\''.$this->page.'\''] = $this->y;
04427                         $this->tMargin = $this->y;
04428                         $this->pagedim[$this->page]['tm'] = $this->tMargin;
04429                         $this->lasth = 0;
04430                         $this->lMargin = $prev_lMargin;
04431                         $this->rMargin = $prev_rMargin;
04432                         $this->cell_padding = $prev_cell_padding;
04433                 }
04434         }
04435 
04443         public function PageNo() {
04444                 return $this->page;
04445         }
04446 
04460         public function AddSpotColor($name, $c, $m, $y, $k) {
04461                 if (!isset($this->spot_colors[$name])) {
04462                         $i = (1 + count($this->spot_colors));
04463                         $this->spot_colors[$name] = array('i' => $i, 'c' => $c, 'm' => $m, 'y' => $y, 'k' => $k);
04464                 }
04465         }
04466 
04474         public function getSpotColor($name) {
04475                 if (isset($this->spot_colors[$name])) {
04476                         return $this->spot_colors[$name];
04477                 }
04478                 $color = preg_replace('/[\s]*/', '', $name); // remove extra spaces
04479                 $color = strtolower($color);
04480                 if (isset($this->spotcolor[$color])) {
04481                         $this->AddSpotColor($this->spotcolor[$color][4], $this->spotcolor[$color][0], $this->spotcolor[$color][1], $this->spotcolor[$color][2], $this->spotcolor[$color][3]);
04482                         return $this->spot_colors[$name];
04483                 }
04484                 return false;
04485         }
04486 
04496         public function setSpotColor($type, $name, $tint=100) {
04497                 $spotcolor = $this->getSpotColor($name);
04498                 if ($spotcolor === false) {
04499                         $this->Error('Undefined spot color: '.$name.', you must add it on the spotcolors.php file.');
04500                 }
04501                 $tint = (max(0, min(100, $tint)) / 100);
04502                 $intcolor = array('C' => $spotcolor['c'], 'M' => $spotcolor['m'], 'Y' => $spotcolor['y'], 'K' => $spotcolor['k'], 'name' => $spotcolor['i']);
04503                 $pdfcolor = sprintf('/CS%d ', $this->spot_colors[$name]['i']);
04504                 switch ($type) {
04505                         case 'draw': {
04506                                 $pdfcolor .= sprintf('CS %.3F SCN', $tint);
04507                                 $this->DrawColor = $pdfcolor;
04508                                 $this->strokecolor = $intcolor;
04509                                 break;
04510                         }
04511                         case 'fill': {
04512                                 $pdfcolor .= sprintf('cs %.3F scn', $tint);
04513                                 $this->FillColor = $pdfcolor;
04514                                 $this->bgcolor = $intcolor;
04515                                 break;
04516                         }
04517                         case 'text': {
04518                                 $pdfcolor .= sprintf('cs %.3F scn', $tint);
04519                                 $this->TextColor = $pdfcolor;
04520                                 $this->fgcolor = $intcolor;
04521                                 break;
04522                         }
04523                 }
04524                 $this->ColorFlag = ($this->FillColor != $this->TextColor);
04525                 if ($this->page > 0) {
04526                         $this->_out($pdfcolor);
04527                 }
04528                 if ($this->inxobj) {
04529                         // we are inside an XObject template
04530                         $this->xobjects[$this->xobjid]['spot_colors'][$name] = $this->spot_colors[$name];
04531                 }
04532                 return $pdfcolor;
04533         }
04534 
04543         public function SetDrawSpotColor($name, $tint=100) {
04544                 $this->setSpotColor('draw', $name, $tint);
04545         }
04546 
04555         public function SetFillSpotColor($name, $tint=100) {
04556                 $this->setSpotColor('fill', $name, $tint);
04557         }
04558 
04567         public function SetTextSpotColor($name, $tint=100) {
04568                 $this->setSpotColor('text', $name, $tint);
04569         }
04570 
04582         public function setColorArray($type, $color, $ret=false) {
04583                 if (is_array($color)) {
04584                         $color = array_values($color);
04585                         // component: grey, RGB red or CMYK cyan
04586                         $r = isset($color[0]) ? $color[0] : -1;
04587                         // component: RGB green or CMYK magenta
04588                         $g = isset($color[1]) ? $color[1] : -1;
04589                         // component: RGB blue or CMYK yellow
04590                         $b = isset($color[2]) ? $color[2] : -1;
04591                         // component: CMYK black
04592                         $k = isset($color[3]) ? $color[3] : -1;
04593                         // spot color name
04594                         $name = isset($color[4]) ? $color[4] : '';
04595                         if ($r >= 0) {
04596                                 return $this->setColor($type, $r, $g, $b, $k, $ret, $name);
04597                         }
04598                 }
04599                 return '';
04600         }
04601 
04613         public function SetDrawColorArray($color, $ret=false) {
04614                 return $this->setColorArray('draw', $color, $ret);
04615         }
04616 
04627         public function SetFillColorArray($color, $ret=false) {
04628                 return $this->setColorArray('fill', $color, $ret);
04629         }
04630 
04640         public function SetTextColorArray($color, $ret=false) {
04641                 return $this->setColorArray('text', $color, $ret);
04642         }
04643 
04657         public function setColor($type, $col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
04658                 // set default values
04659                 if (!is_numeric($col1)) {
04660                         $col1 = 0;
04661                 }
04662                 if (!is_numeric($col2)) {
04663                         $col2 = -1;
04664                 }
04665                 if (!is_numeric($col3)) {
04666                         $col3 = -1;
04667                 }
04668                 if (!is_numeric($col4)) {
04669                         $col4 = -1;
04670                 }
04671                 // set color by case
04672                 $suffix = '';
04673                 if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
04674                         // Grey scale
04675                         $col1 = max(0, min(255, $col1));
04676                         $intcolor = array('G' => $col1);
04677                         $pdfcolor = sprintf('%.3F ', ($col1 / 255));
04678                         $suffix = 'g';
04679                 } elseif ($col4 == -1) {
04680                         // RGB
04681                         $col1 = max(0, min(255, $col1));
04682                         $col2 = max(0, min(255, $col2));
04683                         $col3 = max(0, min(255, $col3));
04684                         $intcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
04685                         $pdfcolor = sprintf('%.3F %.3F %.3F ', ($col1 / 255), ($col2 / 255), ($col3 / 255));
04686                         $suffix = 'rg';
04687                 } else {
04688                         $col1 = max(0, min(100, $col1));
04689                         $col2 = max(0, min(100, $col2));
04690                         $col3 = max(0, min(100, $col3));
04691                         $col4 = max(0, min(100, $col4));
04692                         if (empty($name)) {
04693                                 // CMYK
04694                                 $intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
04695                                 $pdfcolor = sprintf('%.3F %.3F %.3F %.3F ', ($col1 / 100), ($col2 / 100), ($col3 / 100), ($col4 / 100));
04696                                 $suffix = 'k';
04697                         } else {
04698                                 // SPOT COLOR
04699                                 $intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4, 'name' => $name);
04700                                 $this->AddSpotColor($name, $col1, $col2, $col3, $col4);
04701                                 $pdfcolor = $this->setSpotColor($type, $name, 100);
04702                         }
04703                 }
04704                 switch ($type) {
04705                         case 'draw': {
04706                                 $pdfcolor .= strtoupper($suffix);
04707                                 $this->DrawColor = $pdfcolor;
04708                                 $this->strokecolor = $intcolor;
04709                                 break;
04710                         }
04711                         case 'fill': {
04712                                 $pdfcolor .= $suffix;
04713                                 $this->FillColor = $pdfcolor;
04714                                 $this->bgcolor = $intcolor;
04715                                 break;
04716                         }
04717                         case 'text': {
04718                                 $pdfcolor .= $suffix;
04719                                 $this->TextColor = $pdfcolor;
04720                                 $this->fgcolor = $intcolor;
04721                                 break;
04722                         }
04723                 }
04724                 $this->ColorFlag = ($this->FillColor != $this->TextColor);
04725                 if (($type != 'text') AND ($this->page > 0)) {
04726                         if (!$ret) {
04727                                 $this->_out($pdfcolor);
04728                         }
04729                         return $pdfcolor;
04730                 }
04731                 return '';
04732         }
04733 
04747         public function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
04748                 return $this->setColor('draw', $col1, $col2, $col3, $col4, $ret, $name);
04749         }
04750 
04764         public function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
04765                 return $this->setColor('fill', $col1, $col2, $col3, $col4, $ret, $name);
04766         }
04767 
04781         public function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
04782                 return $this->setColor('text', $col1, $col2, $col3, $col4, $ret, $name);
04783         }
04784 
04797         public function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
04798                 return $this->GetArrStringWidth($this->utf8Bidi($this->UTF8StringToArray($s), $s, $this->tmprtl), $fontname, $fontstyle, $fontsize, $getarray);
04799         }
04800 
04813         public function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
04814                 // store current values
04815                 if (!$this->empty_string($fontname)) {
04816                         $prev_FontFamily = $this->FontFamily;
04817                         $prev_FontStyle = $this->FontStyle;
04818                         $prev_FontSizePt = $this->FontSizePt;
04819                         $this->SetFont($fontname, $fontstyle, $fontsize, '', 'default', false);
04820                 }
04821                 // convert UTF-8 array to Latin1 if required
04822                 $sa = $this->UTF8ArrToLatin1($sa);
04823                 $w = 0; // total width
04824                 $wa = array(); // array of characters widths
04825                 foreach ($sa as $ck => $char) {
04826                         // character width
04827                         $cw = $this->GetCharWidth($char, isset($sa[($ck + 1)]));
04828                         $wa[] = $cw;
04829                         $w += $cw;
04830                 }
04831                 // restore previous values
04832                 if (!$this->empty_string($fontname)) {
04833                         $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt, '', 'default', false);
04834                 }
04835                 if ($getarray) {
04836                         return $wa;
04837                 }
04838                 return $w;
04839         }
04840 
04850         public function GetCharWidth($char, $notlast=true) {
04851                 // get raw width
04852                 $chw = $this->getRawCharWidth($char);
04853                 if (($this->font_spacing != 0) AND $notlast) {
04854                         // increase/decrease font spacing
04855                         $chw += $this->font_spacing;
04856                 }
04857                 if ($this->font_stretching != 100) {
04858                         // fixed stretching mode
04859                         $chw *= ($this->font_stretching / 100);
04860                 }
04861                 return $chw;
04862         }
04863 
04872         public function getRawCharWidth($char) {
04873                 if ($char == 173) {
04874                         // SHY character will not be printed
04875                         return (0);
04876                 }
04877                 if (isset($this->CurrentFont['cw'][$char])) {
04878                         $w = $this->CurrentFont['cw'][$char];
04879                 } elseif (isset($this->CurrentFont['dw'])) {
04880                         // default width
04881                         $w = $this->CurrentFont['dw'];
04882                 } elseif (isset($this->CurrentFont['cw'][32])) {
04883                         // default width
04884                         $w = $this->CurrentFont['cw'][32];
04885                 } else {
04886                         $w = 600;
04887                 }
04888                 return ($w * $this->FontSize / 1000);
04889         }
04890 
04898         public function GetNumChars($s) {
04899                 if ($this->isUnicodeFont()) {
04900                         return count($this->UTF8StringToArray($s));
04901                 }
04902                 return strlen($s);
04903         }
04904 
04910         protected function getFontsList() {
04911                 $fontsdir = opendir($this->_getfontpath());
04912                 while (($file = readdir($fontsdir)) !== false) {
04913                         if (substr($file, -4) == '.php') {
04914                                 array_push($this->fontlist, strtolower(basename($file, '.php')));
04915                         }
04916                 }
04917                 closedir($fontsdir);
04918         }
04919 
04933         public function AddFont($family, $style='', $fontfile='', $subset='default') {
04934                 if ($subset === 'default') {
04935                         $subset = $this->font_subsetting;
04936                 }
04937                 if ($this->pdfa_mode) {
04938                         $subset = false;
04939                 }
04940                 if ($this->empty_string($family)) {
04941                         if (!$this->empty_string($this->FontFamily)) {
04942                                 $family = $this->FontFamily;
04943                         } else {
04944                                 $this->Error('Empty font family');
04945                         }
04946                 }
04947                 // move embedded styles on $style
04948                 if (substr($family, -1) == 'I') {
04949                         $style .= 'I';
04950                         $family = substr($family, 0, -1);
04951                 }
04952                 if (substr($family, -1) == 'B') {
04953                         $style .= 'B';
04954                         $family = substr($family, 0, -1);
04955                 }
04956                 // normalize family name
04957                 $family = strtolower($family);
04958                 if ((!$this->isunicode) AND ($family == 'arial')) {
04959                         $family = 'helvetica';
04960                 }
04961                 if (($family == 'symbol') OR ($family == 'zapfdingbats')) {
04962                         $style = '';
04963                 }
04964                 if ($this->pdfa_mode AND (isset($this->CoreFonts[$family]))) {
04965                         // all fonts must be embedded
04966                         $family = 'pdfa'.$family;
04967                 }
04968                 $tempstyle = strtoupper($style);
04969                 $style = '';
04970                 // underline
04971                 if (strpos($tempstyle, 'U') !== false) {
04972                         $this->underline = true;
04973                 } else {
04974                         $this->underline = false;
04975                 }
04976                 // line-through (deleted)
04977                 if (strpos($tempstyle, 'D') !== false) {
04978                         $this->linethrough = true;
04979                 } else {
04980                         $this->linethrough = false;
04981                 }
04982                 // overline
04983                 if (strpos($tempstyle, 'O') !== false) {
04984                         $this->overline = true;
04985                 } else {
04986                         $this->overline = false;
04987                 }
04988                 // bold
04989                 if (strpos($tempstyle, 'B') !== false) {
04990                         $style .= 'B';
04991                 }
04992                 // oblique
04993                 if (strpos($tempstyle, 'I') !== false) {
04994                         $style .= 'I';
04995                 }
04996                 $bistyle = $style;
04997                 $fontkey = $family.$style;
04998                 $font_style = $style.($this->underline ? 'U' : '').($this->linethrough ? 'D' : '').($this->overline ? 'O' : '');
04999                 $fontdata = array('fontkey' => $fontkey, 'family' => $family, 'style' => $font_style);
05000                 // check if the font has been already added
05001                 $fb = $this->getFontBuffer($fontkey);
05002                 if ($fb !== false) {
05003                         if ($this->inxobj) {
05004                                 // we are inside an XObject template
05005                                 $this->xobjects[$this->xobjid]['fonts'][$fontkey] = $fb['i'];
05006                         }
05007                         return $fontdata;
05008                 }
05009                 if (isset($type)) {
05010                         unset($type);
05011                 }
05012                 if (isset($cw)) {
05013                         unset($cw);
05014                 }
05015                 // get specified font directory (if any)
05016                 $fontdir = false;
05017                 if (!$this->empty_string($fontfile)) {
05018                         $fontdir = dirname($fontfile);
05019                         if ($this->empty_string($fontdir) OR ($fontdir == '.')) {
05020                                 $fontdir = '';
05021                         } else {
05022                                 $fontdir .= '/';
05023                         }
05024                 }
05025                 $missing_style = false; // true when the font style variation is missing
05026                 // search and include font file
05027                 if ($this->empty_string($fontfile) OR (!file_exists($fontfile))) {
05028                         // build a standard filenames for specified font
05029                         $tmp_fontfile = str_replace(' ', '', $family).strtolower($style).'.php';
05030                         // search files on various directories
05031                         if (($fontdir !== false) AND file_exists($fontdir.$tmp_fontfile)) {
05032                                 $fontfile = $fontdir.$tmp_fontfile;
05033                         } elseif (file_exists($this->_getfontpath().$tmp_fontfile)) {
05034                                 $fontfile = $this->_getfontpath().$tmp_fontfile;
05035                         } elseif (file_exists($tmp_fontfile)) {
05036                                 $fontfile = $tmp_fontfile;
05037                         } elseif (!$this->empty_string($style)) {
05038                                 $missing_style = true;
05039                                 // try to remove the style part
05040                                 $tmp_fontfile = str_replace(' ', '', $family).'.php';
05041                                 if (($fontdir !== false) AND file_exists($fontdir.$tmp_fontfile)) {
05042                                         $fontfile = $fontdir.$tmp_fontfile;
05043                                 } elseif (file_exists($this->_getfontpath().$tmp_fontfile)) {
05044                                         $fontfile = $this->_getfontpath().$tmp_fontfile;
05045                                 } else {
05046                                         $fontfile = $tmp_fontfile;
05047                                 }
05048                         }
05049                 }
05050                 // include font file
05051                 if (file_exists($fontfile)) {
05052                         include($fontfile);
05053                 } else {
05054                         $this->Error('Could not include font definition file: '.$family.'');
05055                 }
05056                 // check font parameters
05057                 if ((!isset($type)) OR (!isset($cw))) {
05058                         $this->Error('The font definition file has a bad format: '.$fontfile.'');
05059                 }
05060                 // SET default parameters
05061                 if (!isset($file) OR $this->empty_string($file)) {
05062                         $file = '';
05063                 }
05064                 if (!isset($enc) OR $this->empty_string($enc)) {
05065                         $enc = '';
05066                 }
05067                 if (!isset($cidinfo) OR $this->empty_string($cidinfo)) {
05068                         $cidinfo = array('Registry'=>'Adobe', 'Ordering'=>'Identity', 'Supplement'=>0);
05069                         $cidinfo['uni2cid'] = array();
05070                 }
05071                 if (!isset($ctg) OR $this->empty_string($ctg)) {
05072                         $ctg = '';
05073                 }
05074                 if (!isset($desc) OR $this->empty_string($desc)) {
05075                         $desc = array();
05076                 }
05077                 if (!isset($up) OR $this->empty_string($up)) {
05078                         $up = -100;
05079                 }
05080                 if (!isset($ut) OR $this->empty_string($ut)) {
05081                         $ut = 50;
05082                 }
05083                 if (!isset($cw) OR $this->empty_string($cw)) {
05084                         $cw = array();
05085                 }
05086                 if (!isset($dw) OR $this->empty_string($dw)) {
05087                         // set default width
05088                         if (isset($desc['MissingWidth']) AND ($desc['MissingWidth'] > 0)) {
05089                                 $dw = $desc['MissingWidth'];
05090                         } elseif (isset($cw[32])) {
05091                                 $dw = $cw[32];
05092                         } else {
05093                                 $dw = 600;
05094                         }
05095                 }
05096                 ++$this->numfonts;
05097                 if ($type == 'core') {
05098                         $name = $this->CoreFonts[$fontkey];
05099                         $subset = false;
05100                 } elseif (($type == 'TrueType') OR ($type == 'Type1')) {
05101                         $subset = false;
05102                 } elseif ($type == 'TrueTypeUnicode') {
05103                         $enc = 'Identity-H';
05104                 } elseif ($type == 'cidfont0') {
05105                         if ($this->pdfa_mode) {
05106                                 $this->Error('All fonts must be embedded in PDF/A mode!');
05107                         }
05108                 } else {
05109                         $this->Error('Unknow font type: '.$type.'');
05110                 }
05111                 // set name if unset
05112                 if (!isset($name) OR empty($name)) {
05113                         $name = $fontkey;
05114                 }
05115                 // create artificial font style variations if missing (only works with non-embedded fonts)
05116                 if (($type != 'core') AND $missing_style) {
05117                         // style variations
05118                         $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic');
05119                         $name .= $styles[$bistyle];
05120                         // artificial bold
05121                         if (strpos($bistyle, 'B') !== false) {
05122                                 if (isset($desc['StemV'])) {
05123                                         // from normal to bold
05124                                         $desc['StemV'] = round($desc['StemV'] * 1.75);
05125                                 } else {
05126                                         // bold
05127                                         $desc['StemV'] = 123;
05128                                 }
05129                         }
05130                         // artificial italic
05131                         if (strpos($bistyle, 'I') !== false) {
05132                                 if (isset($desc['ItalicAngle'])) {
05133                                         $desc['ItalicAngle'] -= 11;
05134                                 } else {
05135                                         $desc['ItalicAngle'] = -11;
05136                                 }
05137                                 if (isset($desc['Flags'])) {
05138                                         $desc['Flags'] |= 64; //bit 7
05139                                 } else {
05140                                         $desc['Flags'] = 64;
05141                                 }
05142                         }
05143                 }
05144                 // initialize subsetchars to contain default ASCII values (0-255)
05145                 $subsetchars = array_fill(0, 256, true);
05146                 $this->setFontBuffer($fontkey, array('fontkey' => $fontkey, 'i' => $this->numfonts, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'dw' => $dw, 'enc' => $enc, 'cidinfo' => $cidinfo, 'file' => $file, 'ctg' => $ctg, 'subset' => $subset, 'subsetchars' => $subsetchars));
05147                 if ($this->inxobj) {
05148                         // we are inside an XObject template
05149                         $this->xobjects[$this->xobjid]['fonts'][$fontkey] = $this->numfonts;
05150                 }
05151                 if (isset($diff) AND (!empty($diff))) {
05152                         //Search existing encodings
05153                         $d = 0;
05154                         $nb = count($this->diffs);
05155                         for ($i=1; $i <= $nb; ++$i) {
05156                                 if ($this->diffs[$i] == $diff) {
05157                                         $d = $i;
05158                                         break;
05159                                 }
05160                         }
05161                         if ($d == 0) {
05162                                 $d = $nb + 1;
05163                                 $this->diffs[$d] = $diff;
05164                         }
05165                         $this->setFontSubBuffer($fontkey, 'diff', $d);
05166                 }
05167                 if (!$this->empty_string($file)) {
05168                         if (!isset($this->FontFiles[$file])) {
05169                                 if ((strcasecmp($type,'TrueType') == 0) OR (strcasecmp($type, 'TrueTypeUnicode') == 0)) {
05170                                         $this->FontFiles[$file] = array('length1' => $originalsize, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey));
05171                                 } elseif ($type != 'core') {
05172                                         $this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey));
05173                                 }
05174                         } else {
05175                                 // update fontkeys that are sharing this font file
05176                                 $this->FontFiles[$file]['subset'] = ($this->FontFiles[$file]['subset'] AND $subset);
05177                                 if (!in_array($fontkey, $this->FontFiles[$file]['fontkeys'])) {
05178                                         $this->FontFiles[$file]['fontkeys'][] = $fontkey;
05179                                 }
05180                         }
05181                 }
05182                 return $fontdata;
05183         }
05184 
05202         public function SetFont($family, $style='', $size=0, $fontfile='', $subset='default', $out=true) {
05203                 //Select a font; size given in points
05204                 if ($size == 0) {
05205                         $size = $this->FontSizePt;
05206                 }
05207                 // try to add font (if not already added)
05208                 $fontdata = $this->AddFont($family, $style, $fontfile, $subset);
05209                 $this->FontFamily = $fontdata['family'];
05210                 $this->FontStyle = $fontdata['style'];
05211                 $this->CurrentFont = $this->getFontBuffer($fontdata['fontkey']);
05212                 $this->SetFontSize($size, $out);
05213         }
05214 
05223         public function SetFontSize($size, $out=true) {
05224                 // font size in points
05225                 $this->FontSizePt = $size;
05226                 // font size in user units
05227                 $this->FontSize = $size / $this->k;
05228                 // calculate some font metrics
05229                 if (isset($this->CurrentFont['desc']['FontBBox'])) {
05230                         $bbox = explode(' ', substr($this->CurrentFont['desc']['FontBBox'], 1, -1));
05231                         $font_height = ((intval($bbox[3]) - intval($bbox[1])) * $size / 1000);
05232                 } else {
05233                         $font_height = $size * 1.219;
05234                 }
05235                 if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) {
05236                         $font_ascent = ($this->CurrentFont['desc']['Ascent'] * $size / 1000);
05237                 }
05238                 if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] <= 0)) {
05239                         $font_descent = (- $this->CurrentFont['desc']['Descent'] * $size / 1000);
05240                 }
05241                 if (!isset($font_ascent) AND !isset($font_descent)) {
05242                         // core font
05243                         $font_ascent = 0.76 * $font_height;
05244                         $font_descent = $font_height - $font_ascent;
05245                 } elseif (!isset($font_descent)) {
05246                         $font_descent = $font_height - $font_ascent;
05247                 } elseif (!isset($font_ascent)) {
05248                         $font_ascent = $font_height - $font_descent;
05249                 }
05250                 $this->FontAscent = ($font_ascent / $this->k);
05251                 $this->FontDescent = ($font_descent / $this->k);
05252                 if ($out AND ($this->page > 0) AND (isset($this->CurrentFont['i']))) {
05253                         $this->_out(sprintf('BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
05254                 }
05255         }
05256 
05267         public function getFontDescent($font, $style='', $size=0) {
05268                 $fontdata = $this->AddFont($font, $style);
05269                 $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
05270                 if (isset($fontinfo['desc']['Descent']) AND ($fontinfo['desc']['Descent'] <= 0)) {
05271                         $descent = (- $fontinfo['desc']['Descent'] * $size / 1000);
05272                 } else {
05273                         $descent = 1.219 * 0.24 * $size;
05274                 }
05275                 return ($descent / $this->k);
05276         }
05277 
05288         public function getFontAscent($font, $style='', $size=0) {
05289                 $fontdata = $this->AddFont($font, $style);
05290                 $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
05291                 if (isset($fontinfo['desc']['Ascent']) AND ($fontinfo['desc']['Ascent'] > 0)) {
05292                         $ascent = ($fontinfo['desc']['Ascent'] * $size / 1000);
05293                 } else {
05294                         $ascent = 1.219 * 0.76 * $size;
05295                 }
05296                 return ($ascent / $this->k);
05297         }
05298 
05305         public function SetDefaultMonospacedFont($font) {
05306                 $this->default_monospaced_font = $font;
05307         }
05308 
05316         public function AddLink() {
05317                 //Create a new internal link
05318                 $n = count($this->links) + 1;
05319                 $this->links[$n] = array(0, 0);
05320                 return $n;
05321         }
05322 
05332         public function SetLink($link, $y=0, $page=-1) {
05333                 if ($y == -1) {
05334                         $y = $this->y;
05335                 }
05336                 if ($page == -1) {
05337                         $page = $this->page;
05338                 }
05339                 $this->links[$link] = array($page, $y);
05340         }
05341 
05355         public function Link($x, $y, $w, $h, $link, $spaces=0) {
05356                 $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'), $spaces);
05357         }
05358 
05372         public function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text'), $spaces=0) {
05373                 if ($this->inxobj) {
05374                         // store parameters for later use on template
05375                         $this->xobjects[$this->xobjid]['annotations'][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'text' => $text, 'opt' => $opt, 'spaces' => $spaces);
05376                         return;
05377                 }
05378                 if ($x === '') {
05379                         $x = $this->x;
05380                 }
05381                 if ($y === '') {
05382                         $y = $this->y;
05383                 }
05384                 // check page for no-write regions and adapt page margins if necessary
05385                 list($x, $y) = $this->checkPageRegions($h, $x, $y);
05386                 // recalculate coordinates to account for graphic transformations
05387                 if (isset($this->transfmatrix) AND !empty($this->transfmatrix)) {
05388                         for ($i=$this->transfmatrix_key; $i > 0; --$i) {
05389                                 $maxid = count($this->transfmatrix[$i]) - 1;
05390                                 for ($j=$maxid; $j >= 0; --$j) {
05391                                         $ctm = $this->transfmatrix[$i][$j];
05392                                         if (isset($ctm['a'])) {
05393                                                 $x = $x * $this->k;
05394                                                 $y = ($this->h - $y) * $this->k;
05395                                                 $w = $w * $this->k;
05396                                                 $h = $h * $this->k;
05397                                                 // top left
05398                                                 $xt = $x;
05399                                                 $yt = $y;
05400                                                 $x1 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
05401                                                 $y1 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
05402                                                 // top right
05403                                                 $xt = $x + $w;
05404                                                 $yt = $y;
05405                                                 $x2 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
05406                                                 $y2 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
05407                                                 // bottom left
05408                                                 $xt = $x;
05409                                                 $yt = $y - $h;
05410                                                 $x3 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
05411                                                 $y3 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
05412                                                 // bottom right
05413                                                 $xt = $x + $w;
05414                                                 $yt = $y - $h;
05415                                                 $x4 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
05416                                                 $y4 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
05417                                                 // new coordinates (rectangle area)
05418                                                 $x = min($x1, $x2, $x3, $x4);
05419                                                 $y = max($y1, $y2, $y3, $y4);
05420                                                 $w = (max($x1, $x2, $x3, $x4) - $x) / $this->k;
05421                                                 $h = ($y - min($y1, $y2, $y3, $y4)) / $this->k;
05422                                                 $x = $x / $this->k;
05423                                                 $y = $this->h - ($y / $this->k);
05424                                         }
05425                                 }
05426                         }
05427                 }
05428                 if ($this->page <= 0) {
05429                         $page = 1;
05430                 } else {
05431                         $page = $this->page;
05432                 }
05433                 if (!isset($this->PageAnnots[$page])) {
05434                         $this->PageAnnots[$page] = array();
05435                 }
05436                 ++$this->n;
05437                 $this->PageAnnots[$page][] = array('n' => $this->n, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces);
05438                 if (!$this->pdfa_mode) {
05439                         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'])]))) {
05440                                 ++$this->n;
05441                                 $this->embeddedfiles[basename($opt['FS'])] = array('n' => $this->n, 'file' => $opt['FS']);
05442                         }
05443                 }
05444                 // Add widgets annotation's icons
05445                 if (isset($opt['mk']['i']) AND file_exists($opt['mk']['i'])) {
05446                         $this->Image($opt['mk']['i'], '', '', 10, 10, '', '', '', false, 300, '', false, false, 0, false, true);
05447                 }
05448                 if (isset($opt['mk']['ri']) AND file_exists($opt['mk']['ri'])) {
05449                         $this->Image($opt['mk']['ri'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
05450                 }
05451                 if (isset($opt['mk']['ix']) AND file_exists($opt['mk']['ix'])) {
05452                         $this->Image($opt['mk']['ix'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
05453                 }
05454         }
05455 
05462         protected function _putEmbeddedFiles() {
05463                 if ($this->pdfa_mode) {
05464                         // embedded files are not allowed in PDF/A mode
05465                         return;
05466                 }
05467                 reset($this->embeddedfiles);
05468                 foreach ($this->embeddedfiles as $filename => $filedata) {
05469                         $data = file_get_contents($filedata['file']);
05470                         $filter = '';
05471                         if ($this->compress) {
05472                                 $data = gzcompress($data);
05473                                 $filter = ' /Filter /FlateDecode';
05474                         }
05475                         $stream = $this->_getrawstream($data, $filedata['n']);
05476                         $out = $this->_getobj($filedata['n'])."\n";
05477                         $out .= '<< /Type /EmbeddedFile'.$filter.' /Length '.strlen($stream).' >>';
05478                         $out .= ' stream'."\n".$stream."\n".'endstream';
05479                         $out .= "\n".'endobj';
05480                         $this->_out($out);
05481                 }
05482         }
05483 
05507         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) {
05508                 $textrendermode = $this->textrendermode;
05509                 $textstrokewidth = $this->textstrokewidth;
05510                 $this->setTextRenderingMode($fstroke, $ffill, $fclip);
05511                 $this->SetXY($x, $y, $rtloff);
05512                 $this->Cell(0, 0, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height, $calign, $valign);
05513                 // restore previous rendering mode
05514                 $this->textrendermode = $textrendermode;
05515                 $this->textstrokewidth = $textstrokewidth;
05516         }
05517 
05527         public function AcceptPageBreak() {
05528                 if ($this->num_columns > 1) {
05529                         // multi column mode
05530                         if ($this->current_column < ($this->num_columns - 1)) {
05531                                 // go to next column
05532                                 $this->selectColumn($this->current_column + 1);
05533                         } else {
05534                                 // add a new page
05535                                 $this->AddPage();
05536                                 // set first column
05537                                 $this->selectColumn(0);
05538                         }
05539                         // avoid page breaking from checkPageBreak()
05540                         return false;
05541                 }
05542                 return $this->AutoPageBreak;
05543         }
05544 
05554         protected function checkPageBreak($h=0, $y='', $addpage=true) {
05555                 if ($this->empty_string($y)) {
05556                         $y = $this->y;
05557                 }
05558                 $current_page = $this->page;
05559                 if ((($y + $h) > $this->PageBreakTrigger) AND ($this->inPageBody()) AND ($this->AcceptPageBreak())) {
05560                         if ($addpage) {
05561                                 //Automatic page break
05562                                 $x = $this->x;
05563                                 $this->AddPage($this->CurOrientation);
05564                                 $this->y = $this->tMargin;
05565                                 $oldpage = $this->page - 1;
05566                                 if ($this->rtl) {
05567                                         if ($this->pagedim[$this->page]['orm'] != $this->pagedim[$oldpage]['orm']) {
05568                                                 $this->x = $x - ($this->pagedim[$this->page]['orm'] - $this->pagedim[$oldpage]['orm']);
05569                                         } else {
05570                                                 $this->x = $x;
05571                                         }
05572                                 } else {
05573                                         if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
05574                                                 $this->x = $x + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$oldpage]['olm']);
05575                                         } else {
05576                                                 $this->x = $x;
05577                                         }
05578                                 }
05579                         }
05580                         return true;
05581                 }
05582                 if ($current_page != $this->page) {
05583                         // account for columns mode
05584                         return true;
05585                 }
05586                 return false;
05587         }
05588 
05605         public function removeSHY($txt='') {
05606                 $txt = preg_replace('/([\\xc2]{1}[\\xad]{1})/', '', $txt);
05607                 if (!$this->isunicode) {
05608                         $txt = preg_replace('/([\\xad]{1})/', '', $txt);
05609                 }
05610                 return $txt;
05611         }
05612 
05632         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') {
05633                 $prev_cell_margin = $this->cell_margin;
05634                 $prev_cell_padding = $this->cell_padding;
05635                 $this->adjustCellPadding($border);
05636                 if (!$ignore_min_height) {
05637                         $min_cell_height = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
05638                         if ($h < $min_cell_height) {
05639                                 $h = $min_cell_height;
05640                         }
05641                 }
05642                 $this->checkPageBreak($h + $this->cell_margin['T'] + $this->cell_margin['B']);
05643                 $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign));
05644                 $this->cell_padding = $prev_cell_padding;
05645                 $this->cell_margin = $prev_cell_margin;
05646         }
05647 
05668         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') {
05669                 // replace 'NO-BREAK SPACE' (U+00A0) character with a simple space
05670                 $txt = str_replace($this->unichr(160), ' ', $txt);
05671                 $prev_cell_margin = $this->cell_margin;
05672                 $prev_cell_padding = $this->cell_padding;
05673                 $txt = $this->removeSHY($txt);
05674                 $rs = ''; //string to be returned
05675                 $this->adjustCellPadding($border);
05676                 if (!$ignore_min_height) {
05677                         $min_cell_height = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
05678                         if ($h < $min_cell_height) {
05679                                 $h = $min_cell_height;
05680                         }
05681                 }
05682                 $k = $this->k;
05683                 // check page for no-write regions and adapt page margins if necessary
05684                 list($this->x, $this->y) = $this->checkPageRegions($h, $this->x, $this->y);
05685                 if ($this->rtl) {
05686                         $x = $this->x - $this->cell_margin['R'];
05687                 } else {
05688                         $x = $this->x + $this->cell_margin['L'];
05689                 }
05690                 $y = $this->y + $this->cell_margin['T'];
05691                 $prev_font_stretching = $this->font_stretching;
05692                 $prev_font_spacing = $this->font_spacing;
05693                 // cell vertical alignment
05694                 switch ($calign) {
05695                         case 'A': {
05696                                 // font top
05697                                 switch ($valign) {
05698                                         case 'T': {
05699                                                 // top
05700                                                 $y -= $this->cell_padding['T'];
05701                                                 break;
05702                                         }
05703                                         case 'B': {
05704                                                 // bottom
05705                                                 $y -= ($h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent);
05706                                                 break;
05707                                         }
05708                                         default:
05709                                         case 'C':
05710                                         case 'M': {
05711                                                 // center
05712                                                 $y -= (($h - $this->FontAscent - $this->FontDescent) / 2);
05713                                                 break;
05714                                         }
05715                                 }
05716                                 break;
05717                         }
05718                         case 'L': {
05719                                 // font baseline
05720                                 switch ($valign) {
05721                                         case 'T': {
05722                                                 // top
05723                                                 $y -= ($this->cell_padding['T'] + $this->FontAscent);
05724                                                 break;
05725                                         }
05726                                         case 'B': {
05727                                                 // bottom
05728                                                 $y -= ($h - $this->cell_padding['B'] - $this->FontDescent);
05729                                                 break;
05730                                         }
05731                                         default:
05732                                         case 'C':
05733                                         case 'M': {
05734                                                 // center
05735                                                 $y -= (($h + $this->FontAscent - $this->FontDescent) / 2);
05736                                                 break;
05737                                         }
05738                                 }
05739                                 break;
05740                         }
05741                         case 'D': {
05742                                 // font bottom
05743                                 switch ($valign) {
05744                                         case 'T': {
05745                                                 // top
05746                                                 $y -= ($this->cell_padding['T'] + $this->FontAscent + $this->FontDescent);
05747                                                 break;
05748                                         }
05749                                         case 'B': {
05750                                                 // bottom
05751                                                 $y -= ($h - $this->cell_padding['B']);
05752                                                 break;
05753                                         }
05754                                         default:
05755                                         case 'C':
05756                                         case 'M': {
05757                                                 // center
05758                                                 $y -= (($h + $this->FontAscent + $this->FontDescent) / 2);
05759                                                 break;
05760                                         }
05761                                 }
05762                                 break;
05763                         }
05764                         case 'B': {
05765                                 // cell bottom
05766                                 $y -= $h;
05767                                 break;
05768                         }
05769                         case 'C':
05770                         case 'M': {
05771                                 // cell center
05772                                 $y -= ($h / 2);
05773                                 break;
05774                         }
05775                         default:
05776                         case 'T': {
05777                                 // cell top
05778                                 break;
05779                         }
05780                 }
05781                 // text vertical alignment
05782                 switch ($valign) {
05783                         case 'T': {
05784                                 // top
05785                                 $yt = $y + $this->cell_padding['T'];
05786                                 break;
05787                         }
05788                         case 'B': {
05789                                 // bottom
05790                                 $yt = $y + $h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent;
05791                                 break;
05792                         }
05793                         default:
05794                         case 'C':
05795                         case 'M': {
05796                                 // center
05797                                 $yt = $y + (($h - $this->FontAscent - $this->FontDescent) / 2);
05798                                 break;
05799                         }
05800                 }
05801                 $basefonty = $yt + $this->FontAscent;
05802                 if ($this->empty_string($w) OR ($w <= 0)) {
05803                         if ($this->rtl) {
05804                                 $w = $x - $this->lMargin;
05805                         } else {
05806                                 $w = $this->w - $this->rMargin - $x;
05807                         }
05808                 }
05809                 $s = '';
05810                 // fill and borders
05811                 if (is_string($border) AND (strlen($border) == 4)) {
05812                         // full border
05813                         $border = 1;
05814                 }
05815                 if ($fill OR ($border == 1)) {
05816                         if ($fill) {
05817                                 $op = ($border == 1) ? 'B' : 'f';
05818                         } else {
05819                                 $op = 'S';
05820                         }
05821                         if ($this->rtl) {
05822                                 $xk = (($x - $w) * $k);
05823                         } else {
05824                                 $xk = ($x * $k);
05825                         }
05826                         $s .= sprintf('%.2F %.2F %.2F %.2F re %s ', $xk, (($this->h - $y) * $k), ($w * $k), (-$h * $k), $op);
05827                 }
05828                 // draw borders
05829                 $s .= $this->getCellBorder($x, $y, $w, $h, $border);
05830                 if ($txt != '') {
05831                         $txt2 = $txt;
05832                         if ($this->isunicode) {
05833                                 if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
05834                                         $txt2 = $this->UTF8ToLatin1($txt2);
05835                                 } else {
05836                                         $unicode = $this->UTF8StringToArray($txt); // array of UTF-8 unicode values
05837                                         $unicode = $this->utf8Bidi($unicode, '', $this->tmprtl);
05838                                         if (defined('K_THAI_TOPCHARS') AND (K_THAI_TOPCHARS == true)) {
05839                                                 // ---- Fix for bug #2977340 "Incorrect Thai characters position arrangement" ----
05840                                                 // NOTE: this doesn't work with HTML justification
05841                                                 // Symbols that could overlap on the font top (only works in LTR)
05842                                                 $topchar = array(3611, 3613, 3615, 3650, 3651, 3652); // chars that extends on top
05843                                                 $topsym = array(3633, 3636, 3637, 3638, 3639, 3655, 3656, 3657, 3658, 3659, 3660, 3661, 3662); // symbols with top position
05844                                                 $numchars = count($unicode); // number of chars
05845                                                 $unik = 0;
05846                                                 $uniblock = array();
05847                                                 $uniblock[$unik] = array();
05848                                                 $uniblock[$unik][] = $unicode[0];
05849                                                 // resolve overlapping conflicts by splitting the string in several parts
05850                                                 for ($i = 1; $i < $numchars; ++$i) {
05851                                                         // check if symbols overlaps at top
05852                                                         if (in_array($unicode[$i], $topsym) AND (in_array($unicode[($i - 1)], $topsym) OR in_array($unicode[($i - 1)], $topchar))) {
05853                                                                 // move symbols to another array
05854                                                                 ++$unik;
05855                                                                 $uniblock[$unik] = array();
05856                                                                 $uniblock[$unik][] = $unicode[$i];
05857                                                                 ++$unik;
05858                                                                 $uniblock[$unik] = array();
05859                                                                 $unicode[$i] = 0x200b; // Unicode Character 'ZERO WIDTH SPACE' (DEC:8203, U+200B)
05860                                                         } else {
05861                                                                 $uniblock[$unik][] = $unicode[$i];
05862                                                         }
05863                                                 }
05864                                                 // ---- END OF Fix for bug #2977340
05865                                         }
05866                                         $txt2 = $this->arrUTF8ToUTF16BE($unicode, false);
05867                                 }
05868                         }
05869                         $txt2 = $this->_escape($txt2);
05870                         // get current text width (considering general font stretching and spacing)
05871                         $txwidth = $this->GetStringWidth($txt);
05872                         $width = $txwidth;
05873                         // check for stretch mode
05874                         if ($stretch > 0) {
05875                                 // calculate ratio between cell width and text width
05876                                 if ($width <= 0) {
05877                                         $ratio = 1;
05878                                 } else {
05879                                         $ratio = (($w - $this->cell_padding['L'] - $this->cell_padding['R']) / $width);
05880                                 }
05881                                 // check if stretching is required
05882                                 if (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0))) {
05883                                         // the text will be stretched to fit cell width
05884                                         if ($stretch > 2) {
05885                                                 // set new character spacing
05886                                                 $this->font_spacing += ($w - $this->cell_padding['L'] - $this->cell_padding['R'] - $width) / (max(($this->GetNumChars($txt) - 1), 1) * ($this->font_stretching / 100));
05887                                         } else {
05888                                                 // set new horizontal stretching
05889                                                 $this->font_stretching *= $ratio;
05890                                         }
05891                                         // recalculate text width (the text fills the entire cell)
05892                                         $width = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
05893                                         // reset alignment
05894                                         $align = '';
05895                                 }
05896                         }
05897                         if ($this->font_stretching != 100) {
05898                                 // apply font stretching
05899                                 $rs .= sprintf('BT %.2F Tz ET ', $this->font_stretching);
05900                         }
05901                         if ($this->font_spacing != 0) {
05902                                 // increase/decrease font spacing
05903                                 $rs .= sprintf('BT %.2F Tc ET ', ($this->font_spacing * $this->k));
05904                         }
05905                         if ($this->ColorFlag AND ($this->textrendermode < 4)) {
05906                                 $s .= 'q '.$this->TextColor.' ';
05907                         }
05908                         // rendering mode
05909                         $s .= sprintf('BT %d Tr %.2F w ET ', $this->textrendermode, $this->textstrokewidth);
05910                         // count number of spaces
05911                         $ns = substr_count($txt, chr(32));
05912                         // Justification
05913                         $spacewidth = 0;
05914                         if (($align == 'J') AND ($ns > 0)) {
05915                                 if ($this->isUnicodeFont()) {
05916                                         // get string width without spaces
05917                                         $width = $this->GetStringWidth(str_replace(' ', '', $txt));
05918                                         // calculate average space width
05919                                         $spacewidth = -1000 * ($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1) / $this->FontSize;
05920                                         if ($this->font_stretching != 100) {
05921                                                 // word spacing is affected by stretching
05922                                                 $spacewidth /= ($this->font_stretching / 100);
05923                                         }
05924                                         // set word position to be used with TJ operator
05925                                         $txt2 = str_replace(chr(0).chr(32), ') '.sprintf('%.3F', $spacewidth).' (', $txt2);
05926                                         $unicode_justification = true;
05927                                 } else {
05928                                         // get string width
05929                                         $width = $txwidth;
05930                                         // new space width
05931                                         $spacewidth = (($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1)) * $this->k;
05932                                         if ($this->font_stretching != 100) {
05933                                                 // word spacing (Tw) is affected by stretching
05934                                                 $spacewidth /= ($this->font_stretching / 100);
05935                                         }
05936                                         // set word spacing
05937                                         $rs .= sprintf('BT %.3F Tw ET ', $spacewidth);
05938                                 }
05939                                 $width = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
05940                         }
05941                         // replace carriage return characters
05942                         $txt2 = str_replace("\r", ' ', $txt2);
05943                         switch ($align) {
05944                                 case 'C': {
05945                                         $dx = ($w - $width) / 2;
05946                                         break;
05947                                 }
05948                                 case 'R': {
05949                                         if ($this->rtl) {
05950                                                 $dx = $this->cell_padding['R'];
05951                                         } else {
05952                                                 $dx = $w - $width - $this->cell_padding['R'];
05953                                         }
05954                                         break;
05955                                 }
05956                                 case 'L': {
05957                                         if ($this->rtl) {
05958                                                 $dx = $w - $width - $this->cell_padding['L'];
05959                                         } else {
05960                                                 $dx = $this->cell_padding['L'];
05961                                         }
05962                                         break;
05963                                 }
05964                                 case 'J':
05965                                 default: {
05966                                         if ($this->rtl) {
05967                                                 $dx = $this->cell_padding['R'];
05968                                         } else {
05969                                                 $dx = $this->cell_padding['L'];
05970                                         }
05971                                         break;
05972                                 }
05973                         }
05974                         if ($this->rtl) {
05975                                 $xdx = $x - $dx - $width;
05976                         } else {
05977                                 $xdx = $x + $dx;
05978                         }
05979                         $xdk = $xdx * $k;
05980                         // print text
05981                         $s .= sprintf('BT %.2F %.2F Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2);
05982                         if (isset($uniblock)) {
05983                                 // print overlapping characters as separate string
05984                                 $xshift = 0; // horizontal shift
05985                                 $ty = (($this->h - $basefonty + (0.2 * $this->FontSize)) * $k);
05986                                 $spw = (($w - $txwidth - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1));
05987                                 foreach ($uniblock as $uk => $uniarr) {
05988                                         if (($uk % 2) == 0) {
05989                                                 // x space to skip
05990                                                 if ($spacewidth != 0) {
05991                                                         // justification shift
05992                                                         $xshift += (count(array_keys($uniarr, 32)) * $spw);
05993                                                 }
05994                                                 $xshift += $this->GetArrStringWidth($uniarr); // + shift justification
05995                                         } else {
05996                                                 // character to print
05997                                                 $topchr = $this->arrUTF8ToUTF16BE($uniarr, false);
05998                                                 $topchr = $this->_escape($topchr);
05999                                                 $s .= sprintf(' BT %.2F %.2F Td [(%s)] TJ ET', ($xdk + ($xshift * $k)), $ty, $topchr);
06000                                         }
06001                                 }
06002                         }
06003                         if ($this->underline) {
06004                                 $s .= ' '.$this->_dounderlinew($xdx, $basefonty, $width);
06005                         }
06006                         if ($this->linethrough) {
06007                                 $s .= ' '.$this->_dolinethroughw($xdx, $basefonty, $width);
06008                         }
06009                         if ($this->overline) {
06010                                 $s .= ' '.$this->_dooverlinew($xdx, $basefonty, $width);
06011                         }
06012                         if ($this->ColorFlag AND ($this->textrendermode < 4)) {
06013                                 $s .= ' Q';
06014                         }
06015                         if ($link) {
06016                                 $this->Link($xdx, $yt, $width, ($this->FontAscent + $this->FontDescent), $link, $ns);
06017                         }
06018                 }
06019                 // output cell
06020                 if ($s) {
06021                         // output cell
06022                         $rs .= $s;
06023                         if ($this->font_spacing != 0) {
06024                                 // reset font spacing mode
06025                                 $rs .= ' BT 0 Tc ET';
06026                         }
06027                         if ($this->font_stretching != 100) {
06028                                 // reset font stretching mode
06029                                 $rs .= ' BT 100 Tz ET';
06030                         }
06031                 }
06032                 // reset word spacing
06033                 if (!$this->isUnicodeFont() AND ($align == 'J')) {
06034                         $rs .= ' BT 0 Tw ET';
06035                 }
06036                 // reset stretching and spacing
06037                 $this->font_stretching = $prev_font_stretching;
06038                 $this->font_spacing = $prev_font_spacing;
06039                 $this->lasth = $h;
06040                 if ($ln > 0) {
06041                         //Go to the beginning of the next line
06042                         $this->y = $y + $h + $this->cell_margin['B'];
06043                         if ($ln == 1) {
06044                                 if ($this->rtl) {
06045                                         $this->x = $this->w - $this->rMargin;
06046                                 } else {
06047                                         $this->x = $this->lMargin;
06048                                 }
06049                         }
06050                 } else {
06051                         // go left or right by case
06052                         if ($this->rtl) {
06053                                 $this->x = $x - $w - $this->cell_margin['L'];
06054                         } else {
06055                                 $this->x = $x + $w + $this->cell_margin['R'];
06056                         }
06057                 }
06058                 $gstyles = ''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor."\n";
06059                 $rs = $gstyles.$rs;
06060                 $this->cell_padding = $prev_cell_padding;
06061                 $this->cell_margin = $prev_cell_margin;
06062                 return $rs;
06063         }
06064 
06077         protected function getCellBorder($x, $y, $w, $h, $brd) {
06078                 $s = ''; // string to be returned
06079                 if (empty($brd)) {
06080                         return $s;
06081                 }
06082                 if ($brd == 1) {
06083                         $brd = array('LRTB' => true);
06084                 }
06085                 // calculate coordinates for border
06086                 $k = $this->k;
06087                 if ($this->rtl) {
06088                         $xeL = ($x - $w) * $k;
06089                         $xeR = $x * $k;
06090                 } else {
06091                         $xeL = $x * $k;
06092                         $xeR = ($x + $w) * $k;
06093                 }
06094                 $yeL = (($this->h - ($y + $h)) * $k);
06095                 $yeT = (($this->h - $y) * $k);
06096                 $xeT = $xeL;
06097                 $xeB = $xeR;
06098                 $yeR = $yeT;
06099                 $yeB = $yeL;
06100                 if (is_string($brd)) {
06101                         // convert string to array
06102                         $slen = strlen($brd);
06103                         $newbrd = array();
06104                         for ($i = 0; $i < $slen; ++$i) {
06105                                 $newbrd[$brd{$i}] = array('cap' => 'square', 'join' => 'miter');
06106                         }
06107                         $brd = $newbrd;
06108                 }
06109                 if (isset($brd['mode'])) {
06110                         $mode = $brd['mode'];
06111                         unset($brd['mode']);
06112                 } else {
06113                         $mode = 'normal';
06114                 }
06115                 foreach ($brd as $border => $style) {
06116                         if (is_array($style) AND !empty($style)) {
06117                                 // apply border style
06118                                 $prev_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' ';
06119                                 $s .= $this->SetLineStyle($style, true)."\n";
06120                         }
06121                         switch ($mode) {
06122                                 case 'ext': {
06123                                         $off = (($this->LineWidth / 2) * $k);
06124                                         $xL = $xeL - $off;
06125                                         $xR = $xeR + $off;
06126                                         $yT = $yeT + $off;
06127                                         $yL = $yeL - $off;
06128                                         $xT = $xL;
06129                                         $xB = $xR;
06130                                         $yR = $yT;
06131                                         $yB = $yL;
06132                                         $w += $this->LineWidth;
06133                                         $h += $this->LineWidth;
06134                                         break;
06135                                 }
06136                                 case 'int': {
06137                                         $off = ($this->LineWidth / 2) * $k;
06138                                         $xL = $xeL + $off;
06139                                         $xR = $xeR - $off;
06140                                         $yT = $yeT - $off;
06141                                         $yL = $yeL + $off;
06142                                         $xT = $xL;
06143                                         $xB = $xR;
06144                                         $yR = $yT;
06145                                         $yB = $yL;
06146                                         $w -= $this->LineWidth;
06147                                         $h -= $this->LineWidth;
06148                                         break;
06149                                 }
06150                                 case 'normal':
06151                                 default: {
06152                                         $xL = $xeL;
06153                                         $xT = $xeT;
06154                                         $xB = $xeB;
06155                                         $xR = $xeR;
06156                                         $yL = $yeL;
06157                                         $yT = $yeT;
06158                                         $yB = $yeB;
06159                                         $yR = $yeR;
06160                                         break;
06161                                 }
06162                         }
06163                         // draw borders by case
06164                         if (strlen($border) == 4) {
06165                                 $s .= sprintf('%.2F %.2F %.2F %.2F re S ', $xT, $yT, ($w * $k), (-$h * $k));
06166                         } elseif (strlen($border) == 3) {
06167                                 if (strpos($border,'B') === false) { // LTR
06168                                         $s .= sprintf('%.2F %.2F m ', $xL, $yL);
06169                                         $s .= sprintf('%.2F %.2F l ', $xT, $yT);
06170                                         $s .= sprintf('%.2F %.2F l ', $xR, $yR);
06171                                         $s .= sprintf('%.2F %.2F l ', $xB, $yB);
06172                                         $s .= 'S ';
06173                                 } elseif (strpos($border,'L') === false) { // TRB
06174                                         $s .= sprintf('%.2F %.2F m ', $xT, $yT);
06175                                         $s .= sprintf('%.2F %.2F l ', $xR, $yR);
06176                                         $s .= sprintf('%.2F %.2F l ', $xB, $yB);
06177                                         $s .= sprintf('%.2F %.2F l ', $xL, $yL);
06178                                         $s .= 'S ';
06179                                 } elseif (strpos($border,'T') === false) { // RBL
06180                                         $s .= sprintf('%.2F %.2F m ', $xR, $yR);
06181                                         $s .= sprintf('%.2F %.2F l ', $xB, $yB);
06182                                         $s .= sprintf('%.2F %.2F l ', $xL, $yL);
06183                                         $s .= sprintf('%.2F %.2F l ', $xT, $yT);
06184                                         $s .= 'S ';
06185                                 } elseif (strpos($border,'R') === false) { // BLT
06186                                         $s .= sprintf('%.2F %.2F m ', $xB, $yB);
06187                                         $s .= sprintf('%.2F %.2F l ', $xL, $yL);
06188                                         $s .= sprintf('%.2F %.2F l ', $xT, $yT);
06189                                         $s .= sprintf('%.2F %.2F l ', $xR, $yR);
06190                                         $s .= 'S ';
06191                                 }
06192                         } elseif (strlen($border) == 2) {
06193                                 if ((strpos($border,'L') !== false) AND (strpos($border,'T') !== false)) { // LT
06194                                         $s .= sprintf('%.2F %.2F m ', $xL, $yL);
06195                                         $s .= sprintf('%.2F %.2F l ', $xT, $yT);
06196                                         $s .= sprintf('%.2F %.2F l ', $xR, $yR);
06197                                         $s .= 'S ';
06198                                 } elseif ((strpos($border,'T') !== false) AND (strpos($border,'R') !== false)) { // TR
06199                                         $s .= sprintf('%.2F %.2F m ', $xT, $yT);
06200                                         $s .= sprintf('%.2F %.2F l ', $xR, $yR);
06201                                         $s .= sprintf('%.2F %.2F l ', $xB, $yB);
06202                                         $s .= 'S ';
06203                                 } elseif ((strpos($border,'R') !== false) AND (strpos($border,'B') !== false)) { // RB
06204                                         $s .= sprintf('%.2F %.2F m ', $xR, $yR);
06205                                         $s .= sprintf('%.2F %.2F l ', $xB, $yB);
06206                                         $s .= sprintf('%.2F %.2F l ', $xL, $yL);
06207                                         $s .= 'S ';
06208                                 } elseif ((strpos($border,'B') !== false) AND (strpos($border,'L') !== false)) { // BL
06209                                         $s .= sprintf('%.2F %.2F m ', $xB, $yB);
06210                                         $s .= sprintf('%.2F %.2F l ', $xL, $yL);
06211                                         $s .= sprintf('%.2F %.2F l ', $xT, $yT);
06212                                         $s .= 'S ';
06213                                 } elseif ((strpos($border,'L') !== false) AND (strpos($border,'R') !== false)) { // LR
06214                                         $s .= sprintf('%.2F %.2F m ', $xL, $yL);
06215                                         $s .= sprintf('%.2F %.2F l ', $xT, $yT);
06216                                         $s .= 'S ';
06217                                         $s .= sprintf('%.2F %.2F m ', $xR, $yR);
06218                                         $s .= sprintf('%.2F %.2F l ', $xB, $yB);
06219                                         $s .= 'S ';
06220                                 } elseif ((strpos($border,'T') !== false) AND (strpos($border,'B') !== false)) { // TB
06221                                         $s .= sprintf('%.2F %.2F m ', $xT, $yT);
06222                                         $s .= sprintf('%.2F %.2F l ', $xR, $yR);
06223                                         $s .= 'S ';
06224                                         $s .= sprintf('%.2F %.2F m ', $xB, $yB);
06225                                         $s .= sprintf('%.2F %.2F l ', $xL, $yL);
06226                                         $s .= 'S ';
06227                                 }
06228                         } else { // strlen($border) == 1
06229                                 if (strpos($border,'L') !== false) { // L
06230                                         $s .= sprintf('%.2F %.2F m ', $xL, $yL);
06231                                         $s .= sprintf('%.2F %.2F l ', $xT, $yT);
06232                                         $s .= 'S ';
06233                                 } elseif (strpos($border,'T') !== false) { // T
06234                                         $s .= sprintf('%.2F %.2F m ', $xT, $yT);
06235                                         $s .= sprintf('%.2F %.2F l ', $xR, $yR);
06236                                         $s .= 'S ';
06237                                 } elseif (strpos($border,'R') !== false) { // R
06238                                         $s .= sprintf('%.2F %.2F m ', $xR, $yR);
06239                                         $s .= sprintf('%.2F %.2F l ', $xB, $yB);
06240                                         $s .= 'S ';
06241                                 } elseif (strpos($border,'B') !== false) { // B
06242                                         $s .= sprintf('%.2F %.2F m ', $xB, $yB);
06243                                         $s .= sprintf('%.2F %.2F l ', $xL, $yL);
06244                                         $s .= 'S ';
06245                                 }
06246                         }
06247                         if (is_array($style) AND !empty($style)) {
06248                                 // reset border style to previous value
06249                                 $s .= "\n".$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor."\n";
06250                         }
06251                 }
06252                 return $s;
06253         }
06254 
06280         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) {
06281                 $prev_cell_margin = $this->cell_margin;
06282                 $prev_cell_padding = $this->cell_padding;
06283                 // adjust internal padding
06284                 $this->adjustCellPadding($border);
06285                 $mc_padding = $this->cell_padding;
06286                 $mc_margin = $this->cell_margin;
06287                 $this->cell_padding['T'] = 0;
06288                 $this->cell_padding['B'] = 0;
06289                 $this->setCellMargins(0, 0, 0, 0);
06290                 if ($this->empty_string($this->lasth) OR $reseth) {
06291                         // reset row height
06292                         $this->resetLastH();
06293                 }
06294                 if (!$this->empty_string($y)) {
06295                         $this->SetY($y);
06296                 } else {
06297                         $y = $this->GetY();
06298                 }
06299                 $resth = 0;
06300                 if (($h > 0) AND $this->inPageBody() AND (($y + $h + $mc_margin['T'] + $mc_margin['B']) > $this->PageBreakTrigger)) {
06301                         // spit cell in more pages/columns
06302                         $newh = ($this->PageBreakTrigger - $y);
06303                         $resth = ($h - $newh); // cell to be printed on the next page/column
06304                         $h = $newh;
06305                 }
06306                 // get current page number
06307                 $startpage = $this->page;
06308                 // get current column
06309                 $startcolumn = $this->current_column;
06310                 if (!$this->empty_string($x)) {
06311                         $this->SetX($x);
06312                 } else {
06313                         $x = $this->GetX();
06314                 }
06315                 // check page for no-write regions and adapt page margins if necessary
06316                 list($x, $y) = $this->checkPageRegions(0, $x, $y);
06317                 // apply margins
06318                 $oy = $y + $mc_margin['T'];
06319                 if ($this->rtl) {
06320                         $ox = $this->w - $x - $mc_margin['R'];
06321                 } else {
06322                         $ox = $x + $mc_margin['L'];
06323                 }
06324                 $this->x = $ox;
06325                 $this->y = $oy;
06326                 // set width
06327                 if ($this->empty_string($w) OR ($w <= 0)) {
06328                         if ($this->rtl) {
06329                                 $w = $this->x - $this->lMargin - $mc_margin['L'];
06330                         } else {
06331                                 $w = $this->w - $this->x - $this->rMargin - $mc_margin['R'];
06332                         }
06333                 }
06334                 // store original margin values
06335                 $lMargin = $this->lMargin;
06336                 $rMargin = $this->rMargin;
06337                 if ($this->rtl) {
06338                         $this->rMargin = $this->w - $this->x;
06339                         $this->lMargin = $this->x - $w;
06340                 } else {
06341                         $this->lMargin = $this->x;
06342                         $this->rMargin = $this->w - $this->x - $w;
06343                 }
06344                 if ($autopadding) {
06345                         // add top padding
06346                         $this->y += $mc_padding['T'];
06347                 }
06348                 if ($ishtml) { // ******* Write HTML text
06349                         $this->writeHTML($txt, true, false, $reseth, true, $align);
06350                         $nl = 1;
06351                 } else { // ******* Write simple text
06352                         $prev_FontSizePt = $this->FontSizePt;
06353                         // vertical alignment
06354                         if ($maxh > 0) {
06355                                 // get text height
06356                                 $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
06357                                 if ($fitcell) {
06358                                         // try to reduce font size to fit text on cell (use a quick search algorithm)
06359                                         $fmin = 1;
06360                                         $fmax = $this->FontSizePt;
06361                                         $prev_text_height = $text_height;
06362                                         $maxit = 100; // max number of iterations
06363                                         while ($maxit > 0) {
06364                                                 $fmid = (($fmax + $fmin) / 2);
06365                                                 $this->SetFontSize($fmid, false);
06366                                                 $this->resetLastH();
06367                                                 $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
06368                                                 if (($text_height == $maxh) OR (($text_height < $maxh) AND ($fmin >= ($fmax - 0.01)))) {
06369                                                         break;
06370                                                 } elseif ($text_height < $maxh) {
06371                                                         $fmin = $fmid;
06372                                                 } else {
06373                                                         $fmax = $fmid;
06374                                                 }
06375                                                 --$maxit;
06376                                         }
06377                                         $this->SetFontSize($this->FontSizePt);
06378                                 }
06379                                 if ($text_height < $maxh) {
06380                                         if ($valign == 'M') {
06381                                                 // text vertically centered
06382                                                 $this->y += (($maxh - $text_height) / 2);
06383                                         } elseif ($valign == 'B') {
06384                                                 // text vertically aligned on bottom
06385                                                 $this->y += ($maxh - $text_height);
06386                                         }
06387                                 }
06388                         }
06389                         $nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false, true, $maxh, 0, $mc_margin);
06390                         if ($fitcell) {
06391                                 // restore font size
06392                                 $this->SetFontSize($prev_FontSizePt);
06393                         }
06394                 }
06395                 if ($autopadding) {
06396                         // add bottom padding
06397                         $this->y += $mc_padding['B'];
06398                 }
06399                 // Get end-of-text Y position
06400                 $currentY = $this->y;
06401                 // get latest page number
06402                 $endpage = $this->page;
06403                 if ($resth > 0) {
06404                         $skip = ($endpage - $startpage);
06405                         $tmpresth = $resth;
06406                         while ($tmpresth > 0) {
06407                                 if ($skip <= 0) {
06408                                         // add a page (or trig AcceptPageBreak() for multicolumn mode)
06409                                         $this->checkPageBreak($this->PageBreakTrigger + 1);
06410                                 }
06411                                 if ($this->num_columns > 1) {
06412                                         $tmpresth -= ($this->h - $this->y - $this->bMargin);
06413                                 } else {
06414                                         $tmpresth -= ($this->h - $this->tMargin - $this->bMargin);
06415                                 }
06416                                 --$skip;
06417                         }
06418                         $currentY = $this->y;
06419                         $endpage = $this->page;
06420                 }
06421                 // get latest column
06422                 $endcolumn = $this->current_column;
06423                 if ($this->num_columns == 0) {
06424                         $this->num_columns = 1;
06425                 }
06426                 // get border modes
06427                 $border_start = $this->getBorderMode($border, $position='start');
06428                 $border_end = $this->getBorderMode($border, $position='end');
06429                 $border_middle = $this->getBorderMode($border, $position='middle');
06430                 // design borders around HTML cells.
06431                 for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
06432                         $ccode = '';
06433                         $this->setPage($page);
06434                         if ($this->num_columns < 2) {
06435                                 // single-column mode
06436                                 $this->SetX($x);
06437                                 $this->y = $this->tMargin;
06438                         }
06439                         // account for margin changes
06440                         if ($page > $startpage) {
06441                                 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
06442                                         $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
06443                                 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
06444                                         $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
06445                                 }
06446                         }
06447                         if ($startpage == $endpage) {
06448                                 // single page
06449                                 for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
06450                                         $this->selectColumn($column);
06451                                         if ($this->rtl) {
06452                                                 $this->x -= $mc_margin['R'];
06453                                         } else {
06454                                                 $this->x += $mc_margin['L'];
06455                                         }
06456                                         if ($startcolumn == $endcolumn) { // single column
06457                                                 $cborder = $border;
06458                                                 $h = max($h, ($currentY - $oy));
06459                                                 $this->y = $oy;
06460                                         } elseif ($column == $startcolumn) { // first column
06461                                                 $cborder = $border_start;
06462                                                 $this->y = $oy;
06463                                                 $h = $this->h - $this->y - $this->bMargin;
06464                                         } elseif ($column == $endcolumn) { // end column
06465                                                 $cborder = $border_end;
06466                                                 $h = $currentY - $this->y;
06467                                                 if ($resth > $h) {
06468                                                         $h = $resth;
06469                                                 }
06470                                         } else { // middle column
06471                                                 $cborder = $border_middle;
06472                                                 $h = $this->h - $this->y - $this->bMargin;
06473                                                 $resth -= $h;
06474                                         }
06475                                         $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
06476                                 } // end for each column
06477                         } elseif ($page == $startpage) { // first page
06478                                 for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
06479                                         $this->selectColumn($column);
06480                                         if ($this->rtl) {
06481                                                 $this->x -= $mc_margin['R'];
06482                                         } else {
06483                                                 $this->x += $mc_margin['L'];
06484                                         }
06485                                         if ($column == $startcolumn) { // first column
06486                                                 $cborder = $border_start;
06487                                                 $this->y = $oy;
06488                                                 $h = $this->h - $this->y - $this->bMargin;
06489                                         } else { // middle column
06490                                                 $cborder = $border_middle;
06491                                                 $h = $this->h - $this->y - $this->bMargin;
06492                                                 $resth -= $h;
06493                                         }
06494                                         $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
06495                                 } // end for each column
06496                         } elseif ($page == $endpage) { // last page
06497                                 for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
06498                                         $this->selectColumn($column);
06499                                         if ($this->rtl) {
06500                                                 $this->x -= $mc_margin['R'];
06501                                         } else {
06502                                                 $this->x += $mc_margin['L'];
06503                                         }
06504                                         if ($column == $endcolumn) {
06505                                                 // end column
06506                                                 $cborder = $border_end;
06507                                                 $h = $currentY - $this->y;
06508                                                 if ($resth > $h) {
06509                                                         $h = $resth;
06510                                                 }
06511                                         } else {
06512                                                 // middle column
06513                                                 $cborder = $border_middle;
06514                                                 $h = $this->h - $this->y - $this->bMargin;
06515                                                 $resth -= $h;
06516                                         }
06517                                         $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
06518                                 } // end for each column
06519                         } else { // middle page
06520                                 for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
06521                                         $this->selectColumn($column);
06522                                         if ($this->rtl) {
06523                                                 $this->x -= $mc_margin['R'];
06524                                         } else {
06525                                                 $this->x += $mc_margin['L'];
06526                                         }
06527                                         $cborder = $border_middle;
06528                                         $h = $this->h - $this->y - $this->bMargin;
06529                                         $resth -= $h;
06530                                         $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
06531                                 } // end for each column
06532                         }
06533                         if ($cborder OR $fill) {
06534                                 $offsetlen = strlen($ccode);
06535                                 // draw border and fill
06536                                 if ($this->inxobj) {
06537                                         // we are inside an XObject template
06538                                         if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
06539                                                 $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
06540                                                 $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
06541                                                 $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
06542                                         } else {
06543                                                 $pagemark = $this->xobjects[$this->xobjid]['intmrk'];
06544                                                 $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
06545                                         }
06546                                         $pagebuff = $this->xobjects[$this->xobjid]['outdata'];
06547                                         $pstart = substr($pagebuff, 0, $pagemark);
06548                                         $pend = substr($pagebuff, $pagemark);
06549                                         $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
06550                                 } else {
06551                                         if (end($this->transfmrk[$this->page]) !== false) {
06552                                                 $pagemarkkey = key($this->transfmrk[$this->page]);
06553                                                 $pagemark = $this->transfmrk[$this->page][$pagemarkkey];
06554                                                 $this->transfmrk[$this->page][$pagemarkkey] += $offsetlen;
06555                                         } elseif ($this->InFooter) {
06556                                                 $pagemark = $this->footerpos[$this->page];
06557                                                 $this->footerpos[$this->page] += $offsetlen;
06558                                         } else {
06559                                                 $pagemark = $this->intmrk[$this->page];
06560                                                 $this->intmrk[$this->page] += $offsetlen;
06561                                         }
06562                                         $pagebuff = $this->getPageBuffer($this->page);
06563                                         $pstart = substr($pagebuff, 0, $pagemark);
06564                                         $pend = substr($pagebuff, $pagemark);
06565                                         $this->setPageBuffer($this->page, $pstart.$ccode.$pend);
06566                                 }
06567                         }
06568                 } // end for each page
06569                 // Get end-of-cell Y position
06570                 $currentY = $this->GetY();
06571                 // restore previous values
06572                 if ($this->num_columns > 1) {
06573                         $this->selectColumn();
06574                 } else {
06575                         // restore original margins
06576                         $this->lMargin = $lMargin;
06577                         $this->rMargin = $rMargin;
06578                         if ($this->page > $startpage) {
06579                                 // check for margin variations between pages (i.e. booklet mode)
06580                                 $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$startpage]['olm']);
06581                                 $dr = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$startpage]['orm']);
06582                                 if (($dl != 0) OR ($dr != 0)) {
06583                                         $this->lMargin += $dl;
06584                                         $this->rMargin += $dr;
06585                                 }
06586                         }
06587                 }
06588                 if ($ln > 0) {
06589                         //Go to the beginning of the next line
06590                         $this->SetY($currentY + $mc_margin['B']);
06591                         if ($ln == 2) {
06592                                 $this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']);
06593                         }
06594                 } else {
06595                         // go left or right by case
06596                         $this->setPage($startpage);
06597                         $this->y = $y;
06598                         $this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']);
06599                 }
06600                 $this->setContentMark();
06601                 $this->cell_padding = $prev_cell_padding;
06602                 $this->cell_margin = $prev_cell_margin;
06603                 return $nl;
06604         }
06605 
06614         protected function getBorderMode($brd, $position='start') {
06615                 if ((!$this->opencell) OR empty($brd)) {
06616                         return $brd;
06617                 }
06618                 if ($brd == 1) {
06619                         $brd = 'LTRB';
06620                 }
06621                 if (is_string($brd)) {
06622                         // convert string to array
06623                         $slen = strlen($brd);
06624                         $newbrd = array();
06625                         for ($i = 0; $i < $slen; ++$i) {
06626                                 $newbrd[$brd{$i}] = array('cap' => 'square', 'join' => 'miter');
06627                         }
06628                         $brd = $newbrd;
06629                 }
06630                 foreach ($brd as $border => $style) {
06631                         switch ($position) {
06632                                 case 'start': {
06633                                         if (strpos($border, 'B') !== false) {
06634                                                 // remove bottom line
06635                                                 $newkey = str_replace('B', '', $border);
06636                                                 if (strlen($newkey) > 0) {
06637                                                         $brd[$newkey] = $style;
06638                                                 }
06639                                                 unset($brd[$border]);
06640                                         }
06641                                         break;
06642                                 }
06643                                 case 'middle': {
06644                                         if (strpos($border, 'B') !== false) {
06645                                                 // remove bottom line
06646                                                 $newkey = str_replace('B', '', $border);
06647                                                 if (strlen($newkey) > 0) {
06648                                                         $brd[$newkey] = $style;
06649                                                 }
06650                                                 unset($brd[$border]);
06651                                                 $border = $newkey;
06652                                         }
06653                                         if (strpos($border, 'T') !== false) {
06654                                                 // remove bottom line
06655                                                 $newkey = str_replace('T', '', $border);
06656                                                 if (strlen($newkey) > 0) {
06657                                                         $brd[$newkey] = $style;
06658                                                 }
06659                                                 unset($brd[$border]);
06660                                         }
06661                                         break;
06662                                 }
06663                                 case 'end': {
06664                                         if (strpos($border, 'T') !== false) {
06665                                                 // remove bottom line
06666                                                 $newkey = str_replace('T', '', $border);
06667                                                 if (strlen($newkey) > 0) {
06668                                                         $brd[$newkey] = $style;
06669                                                 }
06670                                                 unset($brd[$border]);
06671                                         }
06672                                         break;
06673                                 }
06674                         }
06675                 }
06676                 return $brd;
06677         }
06678 
06692         public function getNumLines($txt, $w=0, $reseth=false, $autopadding=true, $cellpadding='', $border=0) {
06693                 if ($txt === '') {
06694                         // empty string
06695                         return 1;
06696                 }
06697                 // adjust internal padding
06698                 $prev_cell_padding = $this->cell_padding;
06699                 $prev_lasth = $this->lasth;
06700                 if (is_array($cellpadding)) {
06701                         $this->cell_padding = $cellpadding;
06702                 }
06703                 $this->adjustCellPadding($border);
06704                 if ($this->empty_string($w) OR ($w <= 0)) {
06705                         if ($this->rtl) {
06706                                 $w = $this->x - $this->lMargin;
06707                         } else {
06708                                 $w = $this->w - $this->rMargin - $this->x;
06709                         }
06710                 }
06711                 $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
06712                 if ($reseth) {
06713                         // reset row height
06714                         $this->resetLastH();
06715                 }
06716                 $lines = 1;
06717                 $sum = 0;
06718                 $chars = $this->utf8Bidi($this->UTF8StringToArray($txt), $txt, $this->tmprtl);
06719                 $charsWidth = $this->GetArrStringWidth($chars, '', '', 0, true);
06720                 $length = count($chars);
06721                 $lastSeparator = -1;
06722                 for ($i = 0; $i < $length; ++$i) {
06723                         $charWidth = $charsWidth[$i];
06724                         if (preg_match($this->re_spaces, $this->unichr($chars[$i]))) {
06725                                 $lastSeparator = $i;
06726                         }
06727                         if ((($sum + $charWidth) > $wmax) OR ($chars[$i] == 10)) {
06728                                 ++$lines;
06729                                 if ($chars[$i] == 10) {
06730                                         $lastSeparator = -1;
06731                                         $sum = 0;
06732                                 } elseif ($lastSeparator != -1) {
06733                                         $i = $lastSeparator;
06734                                         $lastSeparator = -1;
06735                                         $sum = 0;
06736                                 } else {
06737                                         $sum = $charWidth;
06738                                 }
06739                         } else {
06740                                 $sum += $charWidth;
06741                         }
06742                 }
06743                 if ($chars[($length - 1)] == 10) {
06744                         --$lines;
06745                 }
06746                 $this->cell_padding = $prev_cell_padding;
06747                 $this->lasth = $prev_lasth;
06748                 return $lines;
06749         }
06750 
06798         public function getStringHeight($w, $txt, $reseth=false, $autopadding=true, $cellpadding='', $border=0) {
06799                 // adjust internal padding
06800                 $prev_cell_padding = $this->cell_padding;
06801                 $prev_lasth = $this->lasth;
06802                 if (is_array($cellpadding)) {
06803                         $this->cell_padding = $cellpadding;
06804                 }
06805                 $this->adjustCellPadding($border);
06806                 $lines = $this->getNumLines($txt, $w, $reseth, $autopadding, $cellpadding, $border);
06807                 $height = $lines * ($this->FontSize * $this->cell_height_ratio);
06808                 if ($autopadding) {
06809                         // add top and bottom padding
06810                         $height += ($this->cell_padding['T'] + $this->cell_padding['B']);
06811                 }
06812                 $this->cell_padding = $prev_cell_padding;
06813                 $this->lasth = $prev_lasth;
06814                 return $height;
06815         }
06816 
06835         public function Write($h, $txt, $link='', $fill=false, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0, $wadj=0, $margin='') {
06836                 // check page for no-write regions and adapt page margins if necessary
06837                 list($this->x, $this->y) = $this->checkPageRegions($h, $this->x, $this->y);
06838                 if (strlen($txt) == 0) {
06839                         // fix empty text
06840                         $txt = ' ';
06841                 }
06842                 if ($margin === '') {
06843                         // set default margins
06844                         $margin = $this->cell_margin;
06845                 }
06846                 // remove carriage returns
06847                 $s = str_replace("\r", '', $txt);
06848                 // check if string contains arabic text
06849                 if (preg_match($this->unicode->uni_RE_PATTERN_ARABIC, $s)) {
06850                         $arabic = true;
06851                 } else {
06852                         $arabic = false;
06853                 }
06854                 // check if string contains RTL text
06855                 if ($arabic OR ($this->tmprtl == 'R') OR preg_match($this->unicode->uni_RE_PATTERN_RTL, $s)) {
06856                         $rtlmode = true;
06857                 } else {
06858                         $rtlmode = false;
06859                 }
06860                 // get a char width
06861                 $chrwidth = $this->GetCharWidth(46); // dot character
06862                 // get array of unicode values
06863                 $chars = $this->UTF8StringToArray($s);
06864                 // get array of chars
06865                 $uchars = $this->UTF8ArrayToUniArray($chars);
06866                 // get the number of characters
06867                 $nb = count($chars);
06868                 // replacement for SHY character (minus symbol)
06869                 $shy_replacement = 45;
06870                 $shy_replacement_char = $this->unichr($shy_replacement);
06871                 // widht for SHY replacement
06872                 $shy_replacement_width = $this->GetCharWidth($shy_replacement);
06873                 // max Y
06874                 $maxy = $this->y + $maxh - $h - $this->cell_padding['T'] - $this->cell_padding['B'];
06875                 // page width
06876                 $pw = $w = $this->w - $this->lMargin - $this->rMargin;
06877                 // calculate remaining line width ($w)
06878                 if ($this->rtl) {
06879                         $w = $this->x - $this->lMargin;
06880                 } else {
06881                         $w = $this->w - $this->rMargin - $this->x;
06882                 }
06883                 // max column width
06884                 $wmax = $w - $wadj;
06885                 if (!$firstline) {
06886                         $wmax -= ($this->cell_padding['L'] + $this->cell_padding['R']);
06887                 }
06888                 if ((!$firstline) AND (($chrwidth > $wmax) OR ($this->GetCharWidth($chars[0]) > $wmax))) {
06889                         // a single character do not fit on column
06890                         return '';
06891                 }
06892                 // minimum row height
06893                 $row_height = max($h, $this->FontSize * $this->cell_height_ratio);
06894                 $start_page = $this->page;
06895                 $i = 0; // character position
06896                 $j = 0; // current starting position
06897                 $sep = -1; // position of the last blank space
06898                 $shy = false; // true if the last blank is a soft hypen (SHY)
06899                 $l = 0; // current string length
06900                 $nl = 0; //number of lines
06901                 $linebreak = false;
06902                 $pc = 0; // previous character
06903                 // for each character
06904                 while ($i < $nb) {
06905                         if (($maxh > 0) AND ($this->y >= $maxy) ) {
06906                                 break;
06907                         }
06908                         //Get the current character
06909                         $c = $chars[$i];
06910                         if ($c == 10) { // 10 = "\n" = new line
06911                                 //Explicit line break
06912                                 if ($align == 'J') {
06913                                         if ($this->rtl) {
06914                                                 $talign = 'R';
06915                                         } else {
06916                                                 $talign = 'L';
06917                                         }
06918                                 } else {
06919                                         $talign = $align;
06920                                 }
06921                                 $tmpstr = $this->UniArrSubString($uchars, $j, $i);
06922                                 if ($firstline) {
06923                                         $startx = $this->x;
06924                                         $tmparr = array_slice($chars, $j, ($i - $j));
06925                                         if ($rtlmode) {
06926                                                 $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
06927                                         }
06928                                         $linew = $this->GetArrStringWidth($tmparr);
06929                                         unset($tmparr);
06930                                         if ($this->rtl) {
06931                                                 $this->endlinex = $startx - $linew;
06932                                         } else {
06933                                                 $this->endlinex = $startx + $linew;
06934                                         }
06935                                         $w = $linew;
06936                                         $tmpcellpadding = $this->cell_padding;
06937                                         if ($maxh == 0) {
06938                                                 $this->SetCellPadding(0);
06939                                         }
06940                                 }
06941                                 if ($firstblock AND $this->isRTLTextDir()) {
06942                                         $tmpstr = $this->stringRightTrim($tmpstr);
06943                                 }
06944                                 // Skip newlines at the begining of a page or column
06945                                 if (!empty($tmpstr) OR ($this->y < ($this->PageBreakTrigger - $row_height))) {
06946                                         $this->Cell($w, $h, $tmpstr, 0, 1, $talign, $fill, $link, $stretch);
06947                                 }
06948                                 unset($tmpstr);
06949                                 if ($firstline) {
06950                                         $this->cell_padding = $tmpcellpadding;
06951                                         return ($this->UniArrSubString($uchars, $i));
06952                                 }
06953                                 ++$nl;
06954                                 $j = $i + 1;
06955                                 $l = 0;
06956                                 $sep = -1;
06957                                 $shy = false;
06958                                 // account for margin changes
06959                                 if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) {
06960                                         $this->AcceptPageBreak();
06961                                         if ($this->rtl) {
06962                                                 $this->x -= $margin['R'];
06963                                         } else {
06964                                                 $this->x += $margin['L'];
06965                                         }
06966                                         $this->lMargin += $margin['L'];
06967                                         $this->rMargin += $margin['R'];
06968                                 }
06969                                 $w = $this->getRemainingWidth();
06970                                 $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
06971                         } else {
06972                                 // 160 is the non-breaking space.
06973                                 // 173 is SHY (Soft Hypen).
06974                                 // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
06975                                 // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
06976                                 // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
06977                                 if (($c != 160) AND (($c == 173) OR preg_match($this->re_spaces, $this->unichr($c)))) {
06978                                         // update last blank space position
06979                                         $sep = $i;
06980                                         // check if is a SHY
06981                                         if ($c == 173) {
06982                                                 $shy = true;
06983                                                 if ($pc == 45) {
06984                                                         $tmp_shy_replacement_width = 0;
06985                                                         $tmp_shy_replacement_char = '';
06986                                                 } else {
06987                                                         $tmp_shy_replacement_width = $shy_replacement_width;
06988                                                         $tmp_shy_replacement_char = $shy_replacement_char;
06989                                                 }
06990                                         } else {
06991                                                 $shy = false;
06992                                         }
06993                                 }
06994                                 // update string length
06995                                 if ($this->isUnicodeFont() AND ($arabic)) {
06996                                         // with bidirectional algorithm some chars may be changed affecting the line length
06997                                         // *** very slow ***
06998                                         $l = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, ($i - $j)), '', $this->tmprtl));
06999                                 } else {
07000                                         $l += $this->GetCharWidth($c);
07001                                 }
07002                                 if (($l > $wmax) OR (($c == 173) AND (($l + $tmp_shy_replacement_width) > $wmax)) ) {
07003                                         // we have reached the end of column
07004                                         if ($sep == -1) {
07005                                                 // check if the line was already started
07006                                                 if (($this->rtl AND ($this->x <= ($this->w - $this->rMargin - $chrwidth)))
07007                                                         OR ((!$this->rtl) AND ($this->x >= ($this->lMargin + $chrwidth)))) {
07008                                                         // print a void cell and go to next line
07009                                                         $this->Cell($w, $h, '', 0, 1);
07010                                                         $linebreak = true;
07011                                                         if ($firstline) {
07012                                                                 return ($this->UniArrSubString($uchars, $j));
07013                                                         }
07014                                                 } else {
07015                                                         // truncate the word because do not fit on column
07016                                                         $tmpstr = $this->UniArrSubString($uchars, $j, $i);
07017                                                         if ($firstline) {
07018                                                                 $startx = $this->x;
07019                                                                 $tmparr = array_slice($chars, $j, ($i - $j));
07020                                                                 if ($rtlmode) {
07021                                                                         $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
07022                                                                 }
07023                                                                 $linew = $this->GetArrStringWidth($tmparr);
07024                                                                 unset($tmparr);
07025                                                                 if ($this->rtl) {
07026                                                                         $this->endlinex = $startx - $linew;
07027                                                                 } else {
07028                                                                         $this->endlinex = $startx + $linew;
07029                                                                 }
07030                                                                 $w = $linew;
07031                                                                 $tmpcellpadding = $this->cell_padding;
07032                                                                 if ($maxh == 0) {
07033                                                                         $this->SetCellPadding(0);
07034                                                                 }
07035                                                         }
07036                                                         if ($firstblock AND $this->isRTLTextDir()) {
07037                                                                 $tmpstr = $this->stringRightTrim($tmpstr);
07038                                                         }
07039                                                         $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
07040                                                         unset($tmpstr);
07041                                                         if ($firstline) {
07042                                                                 $this->cell_padding = $tmpcellpadding;
07043                                                                 return ($this->UniArrSubString($uchars, $i));
07044                                                         }
07045                                                         $j = $i;
07046                                                         --$i;
07047                                                 }
07048                                         } else {
07049                                                 // word wrapping
07050                                                 if ($this->rtl AND (!$firstblock) AND ($sep < $i)) {
07051                                                         $endspace = 1;
07052                                                 } else {
07053                                                         $endspace = 0;
07054                                                 }
07055                                                 // check the length of the next string
07056                                                 $strrest = $this->UniArrSubString($uchars, ($sep + $endspace));
07057                                                 $nextstr = preg_split('/'.$this->re_space['p'].'/'.$this->re_space['m'], $this->stringTrim($strrest));
07058                                                 if (isset($nextstr[0]) AND ($this->GetStringWidth($nextstr[0]) > $pw)) {
07059                                                         // truncate the word because do not fit on a full page width
07060                                                         $tmpstr = $this->UniArrSubString($uchars, $j, $i);
07061                                                         if ($firstline) {
07062                                                                 $startx = $this->x;
07063                                                                 $tmparr = array_slice($chars, $j, ($i - $j));
07064                                                                 if ($rtlmode) {
07065                                                                         $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
07066                                                                 }
07067                                                                 $linew = $this->GetArrStringWidth($tmparr);
07068                                                                 unset($tmparr);
07069                                                                 if ($this->rtl) {
07070                                                                         $this->endlinex = $startx - $linew;
07071                                                                 } else {
07072                                                                         $this->endlinex = $startx + $linew;
07073                                                                 }
07074                                                                 $w = $linew;
07075                                                                 $tmpcellpadding = $this->cell_padding;
07076                                                                 if ($maxh == 0) {
07077                                                                         $this->SetCellPadding(0);
07078                                                                 }
07079                                                         }
07080                                                         if ($firstblock AND $this->isRTLTextDir()) {
07081                                                                 $tmpstr = $this->stringRightTrim($tmpstr);
07082                                                         }
07083                                                         $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
07084                                                         unset($tmpstr);
07085                                                         if ($firstline) {
07086                                                                 $this->cell_padding = $tmpcellpadding;
07087                                                                 return ($this->UniArrSubString($uchars, $i));
07088                                                         }
07089                                                         $j = $i;
07090                                                         --$i;
07091                                                 } else {
07092                                                         // word wrapping
07093                                                         if ($shy) {
07094                                                                 // add hypen (minus symbol) at the end of the line
07095                                                                 $shy_width = $tmp_shy_replacement_width;
07096                                                                 if ($this->rtl) {
07097                                                                         $shy_char_left = $tmp_shy_replacement_char;
07098                                                                         $shy_char_right = '';
07099                                                                 } else {
07100                                                                         $shy_char_left = '';
07101                                                                         $shy_char_right = $tmp_shy_replacement_char;
07102                                                                 }
07103                                                         } else {
07104                                                                 $shy_width = 0;
07105                                                                 $shy_char_left = '';
07106                                                                 $shy_char_right = '';
07107                                                         }
07108                                                         $tmpstr = $this->UniArrSubString($uchars, $j, ($sep + $endspace));
07109                                                         if ($firstline) {
07110                                                                 $startx = $this->x;
07111                                                                 $tmparr = array_slice($chars, $j, (($sep + $endspace) - $j));
07112                                                                 if ($rtlmode) {
07113                                                                         $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
07114                                                                 }
07115                                                                 $linew = $this->GetArrStringWidth($tmparr);
07116                                                                 unset($tmparr);
07117                                                                 if ($this->rtl) {
07118                                                                         $this->endlinex = $startx - $linew - $shy_width;
07119                                                                 } else {
07120                                                                         $this->endlinex = $startx + $linew + $shy_width;
07121                                                                 }
07122                                                                 $w = $linew;
07123                                                                 $tmpcellpadding = $this->cell_padding;
07124                                                                 if ($maxh == 0) {
07125                                                                         $this->SetCellPadding(0);
07126                                                                 }
07127                                                         }
07128                                                         // print the line
07129                                                         if ($firstblock AND $this->isRTLTextDir()) {
07130                                                                 $tmpstr = $this->stringRightTrim($tmpstr);
07131                                                         }
07132                                                         $this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch);
07133                                                         unset($tmpstr);
07134                                                         if ($firstline) {
07135                                                                 // return the remaining text
07136                                                                 $this->cell_padding = $tmpcellpadding;
07137                                                                 return ($this->UniArrSubString($uchars, ($sep + $endspace)));
07138                                                         }
07139                                                         $i = $sep;
07140                                                         $sep = -1;
07141                                                         $shy = false;
07142                                                         $j = ($i+1);
07143                                                 }
07144                                         }
07145                                         // account for margin changes
07146                                         if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) {
07147                                                 $this->AcceptPageBreak();
07148                                                 if ($this->rtl) {
07149                                                         $this->x -= $margin['R'];
07150                                                 } else {
07151                                                         $this->x += $margin['L'];
07152                                                 }
07153                                                 $this->lMargin += $margin['L'];
07154                                                 $this->rMargin += $margin['R'];
07155                                         }
07156                                         $w = $this->getRemainingWidth();
07157                                         $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
07158                                         if ($linebreak) {
07159                                                 $linebreak = false;
07160                                         } else {
07161                                                 ++$nl;
07162                                                 $l = 0;
07163                                         }
07164                                 }
07165                         }
07166                         // save last character
07167                         $pc = $c;
07168                         ++$i;
07169                 } // end while i < nb
07170                 // print last substring (if any)
07171                 if ($l > 0) {
07172                         switch ($align) {
07173                                 case 'J':
07174                                 case 'C': {
07175                                         $w = $w;
07176                                         break;
07177                                 }
07178                                 case 'L': {
07179                                         if ($this->rtl) {
07180                                                 $w = $w;
07181                                         } else {
07182                                                 $w = $l;
07183                                         }
07184                                         break;
07185                                 }
07186                                 case 'R': {
07187                                         if ($this->rtl) {
07188                                                 $w = $l;
07189                                         } else {
07190                                                 $w = $w;
07191                                         }
07192                                         break;
07193                                 }
07194                                 default: {
07195                                         $w = $l;
07196                                         break;
07197                                 }
07198                         }
07199                         $tmpstr = $this->UniArrSubString($uchars, $j, $nb);
07200                         if ($firstline) {
07201                                 $startx = $this->x;
07202                                 $tmparr = array_slice($chars, $j, ($nb - $j));
07203                                 if ($rtlmode) {
07204                                         $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
07205                                 }
07206                                 $linew = $this->GetArrStringWidth($tmparr);
07207                                 unset($tmparr);
07208                                 if ($this->rtl) {
07209                                         $this->endlinex = $startx - $linew;
07210                                 } else {
07211                                         $this->endlinex = $startx + $linew;
07212                                 }
07213                                 $w = $linew;
07214                                 $tmpcellpadding = $this->cell_padding;
07215                                 if ($maxh == 0) {
07216                                         $this->SetCellPadding(0);
07217                                 }
07218                         }
07219                         if ($firstblock AND $this->isRTLTextDir()) {
07220                                 $tmpstr = $this->stringRightTrim($tmpstr);
07221                         }
07222                         $this->Cell($w, $h, $tmpstr, 0, $ln, $align, $fill, $link, $stretch);
07223                         unset($tmpstr);
07224                         if ($firstline) {
07225                                 $this->cell_padding = $tmpcellpadding;
07226                                 return ($this->UniArrSubString($uchars, $nb));
07227                         }
07228                         ++$nl;
07229                 }
07230                 if ($firstline) {
07231                         return '';
07232                 }
07233                 return $nl;
07234         }
07235 
07241         protected function getRemainingWidth() {
07242                 list($this->x, $this->y) = $this->checkPageRegions(0, $this->x, $this->y);
07243                 if ($this->rtl) {
07244                         return ($this->x - $this->lMargin);
07245                 } else {
07246                         return ($this->w - $this->rMargin - $this->x);
07247                 }
07248         }
07249 
07258         public function UTF8ArrSubString($strarr, $start='', $end='') {
07259                 if (strlen($start) == 0) {
07260                         $start = 0;
07261                 }
07262                 if (strlen($end) == 0) {
07263                         $end = count($strarr);
07264                 }
07265                 $string = '';
07266                 for ($i=$start; $i < $end; ++$i) {
07267                         $string .= $this->unichr($strarr[$i]);
07268                 }
07269                 return $string;
07270         }
07271 
07281         public function UniArrSubString($uniarr, $start='', $end='') {
07282                 if (strlen($start) == 0) {
07283                         $start = 0;
07284                 }
07285                 if (strlen($end) == 0) {
07286                         $end = count($uniarr);
07287                 }
07288                 $string = '';
07289                 for ($i=$start; $i < $end; ++$i) {
07290                         $string .= $uniarr[$i];
07291                 }
07292                 return $string;
07293         }
07294 
07302         public function UTF8ArrayToUniArray($ta) {
07303                 return array_map(array($this, 'unichr'), $ta);
07304         }
07305 
07314         public function unichr($c) {
07315                 if (!$this->isunicode) {
07316                         return chr($c);
07317                 } elseif ($c <= 0x7F) {
07318                         // one byte
07319                         return chr($c);
07320                 } elseif ($c <= 0x7FF) {
07321                         // two bytes
07322                         return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F);
07323                 } elseif ($c <= 0xFFFF) {
07324                         // three bytes
07325                         return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
07326                 } elseif ($c <= 0x10FFFF) {
07327                         // four bytes
07328                         return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
07329                 } else {
07330                         return '';
07331                 }
07332         }
07333 
07341         public function getImageFileType($imgfile, $iminfo=array()) {
07342                 $type = '';
07343                 if (isset($iminfo['mime']) AND !empty($iminfo['mime'])) {
07344                         $mime = explode('/', $iminfo['mime']);
07345                         if ((count($mime) > 1) AND ($mime[0] == 'image') AND (!empty($mime[1]))) {
07346                                 $type = strtolower(trim($mime[1]));
07347                         }
07348                 }
07349                 if (empty($type)) {
07350                         $fileinfo = pathinfo($imgfile);
07351                         if (isset($fileinfo['extension']) AND (!$this->empty_string($fileinfo['extension']))) {
07352                                 $type = strtolower(trim($fileinfo['extension']));
07353                         }
07354                 }
07355                 if ($type == 'jpg') {
07356                         $type = 'jpeg';
07357                 }
07358                 return $type;
07359         }
07360 
07372         protected function fitBlock($w, $h, $x, $y, $fitonpage=false) {
07373                 if ($w <= 0) {
07374                         // set maximum width
07375                         $w = ($this->w - $this->lMargin - $this->rMargin);
07376                 }
07377                 if ($h <= 0) {
07378                         // set maximum height
07379                         $h = ($this->PageBreakTrigger - $this->tMargin);
07380                 }
07381                 // resize the block to be vertically contained on a single page or single column
07382                 if ($fitonpage OR $this->AutoPageBreak) {
07383                         $ratio_wh = ($w / $h);
07384                         if ($h > ($this->PageBreakTrigger - $this->tMargin)) {
07385                                 $h = $this->PageBreakTrigger - $this->tMargin;
07386                                 $w = ($h * $ratio_wh);
07387                         }
07388                         // resize the block to be horizontally contained on a single page or single column
07389                         if ($fitonpage) {
07390                                 $maxw = ($this->w - $this->lMargin - $this->rMargin);
07391                                 if ($w > $maxw) {
07392                                         $w = $maxw;
07393                                         $h = ($w / $ratio_wh);
07394                                 }
07395                         }
07396                 }
07397                 // Check whether we need a new page or new column first as this does not fit
07398                 $prev_x = $this->x;
07399                 $prev_y = $this->y;
07400                 if ($this->checkPageBreak($h, $y) OR ($this->y < $prev_y)) {
07401                         $y = $this->y;
07402                         if ($this->rtl) {
07403                                 $x += ($prev_x - $this->x);
07404                         } else {
07405                                 $x += ($this->x - $prev_x);
07406                         }
07407                         $this->newline = true;
07408                 }
07409                 // resize the block to be contained on the remaining available page or column space
07410                 if ($fitonpage) {
07411                         $ratio_wh = ($w / $h);
07412                         if (($y + $h) > $this->PageBreakTrigger) {
07413                                 $h = $this->PageBreakTrigger - $y;
07414                                 $w = ($h * $ratio_wh);
07415                         }
07416                         if ((!$this->rtl) AND (($x + $w) > ($this->w - $this->rMargin))) {
07417                                 $w = $this->w - $this->rMargin - $x;
07418                                 $h = ($w / $ratio_wh);
07419                         } elseif (($this->rtl) AND (($x - $w) < ($this->lMargin))) {
07420                                 $w = $x - $this->lMargin;
07421                                 $h = ($w / $ratio_wh);
07422                         }
07423                 }
07424                 return array($w, $h, $x, $y);
07425         }
07426 
07461         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()) {
07462                 if ($x === '') {
07463                         $x = $this->x;
07464                 }
07465                 if ($y === '') {
07466                         $y = $this->y;
07467                 }
07468                 // check page for no-write regions and adapt page margins if necessary
07469                 list($x, $y) = $this->checkPageRegions($h, $x, $y);
07470                 $cached_file = false; // true when the file is cached
07471                 $exurl = ''; // external streams
07472                 // check if we are passing an image as file or string
07473                 if ($file{0} === '@') {
07474                         // image from string
07475                         $imgdata = substr($file, 1);
07476                         $file = K_PATH_CACHE.'img_'.md5($imgdata);
07477                         $fp = fopen($file, 'w');
07478                         fwrite($fp, $imgdata);
07479                         fclose($fp);
07480                         unset($imgdata);
07481                         $cached_file = true;
07482                         $imsize = @getimagesize($file);
07483                         if ($imsize === FALSE) {
07484                                 unlink($file);
07485                                 $cached_file = false;
07486                         }
07487                 } else { // image file
07488                         if ($file{0} === '*') {
07489                                 // image as external stream
07490                                 $file = substr($file, 1);
07491                                 $exurl = $file;
07492                         }
07493                         // check if is local file
07494                         if (!@file_exists($file)) {
07495                                 // encode spaces on filename (file is probably an URL)
07496                                 $file = str_replace(' ', '%20', $file);
07497                         }
07498                         if (@file_exists($file)) {
07499                                 // get image dimensions
07500                                 $imsize = @getimagesize($file);
07501                         } else {
07502                                 $imsize = false;
07503                         }
07504                         if ($imsize === FALSE) {
07505                                 if (function_exists('curl_init')) {
07506                                         // try to get remote file data using cURL
07507                                         $cs = curl_init(); // curl session
07508                                         curl_setopt($cs, CURLOPT_URL, $file);
07509                                         curl_setopt($cs, CURLOPT_BINARYTRANSFER, true);
07510                                         curl_setopt($cs, CURLOPT_FAILONERROR, true);
07511                                         curl_setopt($cs, CURLOPT_RETURNTRANSFER, true);
07512                                         curl_setopt($cs, CURLOPT_FOLLOWLOCATION, true);
07513                                         curl_setopt($cs, CURLOPT_CONNECTTIMEOUT, 5);
07514                                         curl_setopt($cs, CURLOPT_TIMEOUT, 30);
07515                                         curl_setopt($cs, CURLOPT_SSL_VERIFYPEER, false);
07516                                         curl_setopt($cs, CURLOPT_SSL_VERIFYHOST, false);
07517                                         curl_setopt($cs, CURLOPT_USERAGENT, 'TCPDF');
07518                                         $imgdata = curl_exec($cs);
07519                                         curl_close($cs);
07520                                         if ($imgdata !== FALSE) {
07521                                                 // copy image to cache
07522                                                 $file = K_PATH_CACHE.'img_'.md5($imgdata);
07523                                                 $fp = fopen($file, 'w');
07524                                                 fwrite($fp, $imgdata);
07525                                                 fclose($fp);
07526                                                 unset($imgdata);
07527                                                 $cached_file = true;
07528                                                 $imsize = @getimagesize($file);
07529                                                 if ($imsize === FALSE) {
07530                                                         unlink($file);
07531                                                         $cached_file = false;
07532                                                 }
07533                                         }
07534                                 } elseif (($w > 0) AND ($h > 0)) {
07535                                         // get measures from specified data
07536                                         $pw = $this->getHTMLUnitToUnits($w, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
07537                                         $ph = $this->getHTMLUnitToUnits($h, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
07538                                         $imsize = array($pw, $ph);
07539                                 }
07540                         }
07541                 }
07542                 if ($imsize === FALSE) {
07543                         if (substr($file, 0, -34) == K_PATH_CACHE.'msk') { // mask file
07544                                 // get measures from specified data
07545                                 $pw = $this->getHTMLUnitToUnits($w, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
07546                                 $ph = $this->getHTMLUnitToUnits($h, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
07547                                 $imsize = array($pw, $ph);
07548                         } else {
07549                                 $this->Error('[Image] Unable to get image: '.$file);
07550                         }
07551                 }
07552                 // get original image width and height in pixels
07553                 list($pixw, $pixh) = $imsize;
07554                 // calculate image width and height on document
07555                 if (($w <= 0) AND ($h <= 0)) {
07556                         // convert image size to document unit
07557                         $w = $this->pixelsToUnits($pixw);
07558                         $h = $this->pixelsToUnits($pixh);
07559                 } elseif ($w <= 0) {
07560                         $w = $h * $pixw / $pixh;
07561                 } elseif ($h <= 0) {
07562                         $h = $w * $pixh / $pixw;
07563                 } elseif (($fitbox !== false) AND ($w > 0) AND ($h > 0)) {
07564                         if (strlen($fitbox) !== 2) {
07565                                 // set default alignment
07566                                 $fitbox = '--';
07567                         }
07568                         // scale image dimensions proportionally to fit within the ($w, $h) box
07569                         if ((($w * $pixh) / ($h * $pixw)) < 1) {
07570                                 // store current height
07571                                 $oldh = $h;
07572                                 // calculate new height
07573                                 $h = $w * $pixh / $pixw;
07574                                 // height difference
07575                                 $hdiff = ($oldh - $h);
07576                                 // vertical alignment
07577                                 switch (strtoupper($fitbox{1})) {
07578                                         case 'T': {
07579                                                 break;
07580                                         }
07581                                         case 'M': {
07582                                                 $y += ($hdiff / 2);
07583                                                 break;
07584                                         }
07585                                         case 'B': {
07586                                                 $y += $hdiff;
07587                                                 break;
07588                                         }
07589                                 }
07590                         } else {
07591                                 // store current width
07592                                 $oldw = $w;
07593                                 // calculate new width
07594                                 $w = $h * $pixw / $pixh;
07595                                 // width difference
07596                                 $wdiff = ($oldw - $w);
07597                                 // horizontal alignment
07598                                 switch (strtoupper($fitbox{0})) {
07599                                         case 'L': {
07600                                                 if ($this->rtl) {
07601                                                         $x -= $wdiff;
07602                                                 }
07603                                                 break;
07604                                         }
07605                                         case 'C': {
07606                                                 if ($this->rtl) {
07607                                                         $x -= ($wdiff / 2);
07608                                                 } else {
07609                                                         $x += ($wdiff / 2);
07610                                                 }
07611                                                 break;
07612                                         }
07613                                         case 'R': {
07614                                                 if (!$this->rtl) {
07615                                                         $x += $wdiff;
07616                                                 }
07617                                                 break;
07618                                         }
07619                                 }
07620                         }
07621                 }
07622                 // fit the image on available space
07623                 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
07624                 // calculate new minimum dimensions in pixels
07625                 $neww = round($w * $this->k * $dpi / $this->dpi);
07626                 $newh = round($h * $this->k * $dpi / $this->dpi);
07627                 // check if resize is necessary (resize is used only to reduce the image)
07628                 $newsize = ($neww * $newh);
07629                 $pixsize = ($pixw * $pixh);
07630                 if (intval($resize) == 2) {
07631                         $resize = true;
07632                 } elseif ($newsize >= $pixsize) {
07633                         $resize = false;
07634                 }
07635                 // check if image has been already added on document
07636                 $newimage = true;
07637                 if (in_array($file, $this->imagekeys)) {
07638                         $newimage = false;
07639                         // get existing image data
07640                         $info = $this->getImageBuffer($file);
07641                         if (substr($file, 0, -34) != K_PATH_CACHE.'msk') {
07642                                 // check if the newer image is larger
07643                                 $oldsize = ($info['w'] * $info['h']);
07644                                 if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
07645                                         $newimage = true;
07646                                 }
07647                         }
07648                 } elseif (substr($file, 0, -34) != K_PATH_CACHE.'msk') {
07649                         // check for cached images with alpha channel
07650                         $filehash = md5($file);
07651                         $tempfile_plain = K_PATH_CACHE.'mskp_'.$filehash;
07652                         $tempfile_alpha = K_PATH_CACHE.'mska_'.$filehash;
07653                         if (in_array($tempfile_plain, $this->imagekeys)) {
07654                                 // get existing image data
07655                                 $info = $this->getImageBuffer($tempfile_plain);
07656                                 // check if the newer image is larger
07657                                 $oldsize = ($info['w'] * $info['h']);
07658                                 if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
07659                                         $newimage = true;
07660                                 } else {
07661                                         $newimage = false;
07662                                         // embed mask image
07663                                         $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
07664                                         // embed image, masked with previously embedded mask
07665                                         return $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
07666                                 }
07667                         }
07668                 }
07669                 if ($newimage) {
07670                         //First use of image, get info
07671                         $type = strtolower($type);
07672                         if ($type == '') {
07673                                 $type = $this->getImageFileType($file, $imsize);
07674                         } elseif ($type == 'jpg') {
07675                                 $type = 'jpeg';
07676                         }
07677                         $mqr = $this->get_mqr();
07678                         $this->set_mqr(false);
07679                         // Specific image handlers
07680                         $mtd = '_parse'.$type;
07681                         // GD image handler function
07682                         $gdfunction = 'imagecreatefrom'.$type;
07683                         $info = false;
07684                         if ((method_exists($this, $mtd)) AND (!($resize AND (function_exists($gdfunction) OR extension_loaded('imagick'))))) {
07685                                 // TCPDF image functions
07686                                 $info = $this->$mtd($file);
07687                                 if ($info == 'pngalpha') {
07688                                         return $this->ImagePngAlpha($file, $x, $y, $pixw, $pixh, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign, $filehash);
07689                                 }
07690                         }
07691                         if (!$info) {
07692                                 if (function_exists($gdfunction)) {
07693                                         // GD library
07694                                         $img = $gdfunction($file);
07695                                         if ($resize) {
07696                                                 $imgr = imagecreatetruecolor($neww, $newh);
07697                                                 if (($type == 'gif') OR ($type == 'png')) {
07698                                                         $imgr = $this->_setGDImageTransparency($imgr, $img);
07699                                                 }
07700                                                 imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh);
07701                                                 if (($type == 'gif') OR ($type == 'png')) {
07702                                                         $info = $this->_toPNG($imgr);
07703                                                 } else {
07704                                                         $info = $this->_toJPEG($imgr);
07705                                                 }
07706                                         } else {
07707                                                 if (($type == 'gif') OR ($type == 'png')) {
07708                                                         $info = $this->_toPNG($img);
07709                                                 } else {
07710                                                         $info = $this->_toJPEG($img);
07711                                                 }
07712                                         }
07713                                 } elseif (extension_loaded('imagick')) {
07714                                         // ImageMagick library
07715                                         $img = new Imagick();
07716                                         if ($type == 'SVG') {
07717                                                 // get SVG file content
07718                                                 $svgimg = file_get_contents($file);
07719                                                 // get width and height
07720                                                 $regs = array();
07721                                                 if (preg_match('/<svg([^>]*)>/si', $svgimg, $regs)) {
07722                                                         $svgtag = $regs[1];
07723                                                         $tmp = array();
07724                                                         if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
07725                                                                 $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
07726                                                                 $owu = sprintf('%.3F', ($ow * $dpi / 72)).$this->pdfunit;
07727                                                                 $svgtag = preg_replace('/[\s]+width[\s]*=[\s]*"[^"]*"/si', ' width="'.$owu.'"', $svgtag, 1);
07728                                                         } else {
07729                                                                 $ow = $w;
07730                                                         }
07731                                                         $tmp = array();
07732                                                         if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
07733                                                                 $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
07734                                                                 $ohu = sprintf('%.3F', ($oh * $dpi / 72)).$this->pdfunit;
07735                                                                 $svgtag = preg_replace('/[\s]+height[\s]*=[\s]*"[^"]*"/si', ' height="'.$ohu.'"', $svgtag, 1);
07736                                                         } else {
07737                                                                 $oh = $h;
07738                                                         }
07739                                                         $tmp = array();
07740                                                         if (!preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $svgtag, $tmp)) {
07741                                                                 $vbw = ($ow * $this->imgscale * $this->k);
07742                                                                 $vbh = ($oh * $this->imgscale * $this->k);
07743                                                                 $vbox = sprintf(' viewBox="0 0 %.3F %.3F" ', $vbw, $vbh);
07744                                                                 $svgtag = $vbox.$svgtag;
07745                                                         }
07746                                                         $svgimg = preg_replace('/<svg([^>]*)>/si', '<svg'.$svgtag.'>', $svgimg, 1);
07747                                                 }
07748                                                 $img->readImageBlob($svgimg);
07749                                         } else {
07750                                                 $img->readImage($file);
07751                                         }
07752                                         if ($resize) {
07753                                                 $img->resizeImage($neww, $newh, 10, 1, false);
07754                                         }
07755                                         $img->setCompressionQuality($this->jpeg_quality);
07756                                         $img->setImageFormat('jpeg');
07757                                         $tempname = tempnam(K_PATH_CACHE, 'jpg_');
07758                                         $img->writeImage($tempname);
07759                                         $info = $this->_parsejpeg($tempname);
07760                                         unlink($tempname);
07761                                         $img->destroy();
07762                                 } else {
07763                                         return;
07764                                 }
07765                         }
07766                         if ($info === false) {
07767                                 //If false, we cannot process image
07768                                 return;
07769                         }
07770                         $this->set_mqr($mqr);
07771                         if ($ismask) {
07772                                 // force grayscale
07773                                 $info['cs'] = 'DeviceGray';
07774                         }
07775                         $info['i'] = $this->numimages;
07776                         if (!in_array($file, $this->imagekeys)) {
07777                                 ++$info['i'];
07778                         }
07779                         if ($imgmask !== false) {
07780                                 $info['masked'] = $imgmask;
07781                         }
07782                         if (!empty($exurl)) {
07783                                 $info['exurl'] = $exurl;
07784                         }
07785                         // array of alternative images
07786                         $info['altimgs'] = $altimgs;
07787                         // add image to document
07788                         $this->setImageBuffer($file, $info);
07789                 }
07790                 if ($cached_file) {
07791                         // remove cached file
07792                         unlink($file);
07793                 }
07794                 // set alignment
07795                 $this->img_rb_y = $y + $h;
07796                 // set alignment
07797                 if ($this->rtl) {
07798                         if ($palign == 'L') {
07799                                 $ximg = $this->lMargin;
07800                         } elseif ($palign == 'C') {
07801                                 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
07802                         } elseif ($palign == 'R') {
07803                                 $ximg = $this->w - $this->rMargin - $w;
07804                         } else {
07805                                 $ximg = $x - $w;
07806                         }
07807                         $this->img_rb_x = $ximg;
07808                 } else {
07809                         if ($palign == 'L') {
07810                                 $ximg = $this->lMargin;
07811                         } elseif ($palign == 'C') {
07812                                 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
07813                         } elseif ($palign == 'R') {
07814                                 $ximg = $this->w - $this->rMargin - $w;
07815                         } else {
07816                                 $ximg = $x;
07817                         }
07818                         $this->img_rb_x = $ximg + $w;
07819                 }
07820                 if ($ismask OR $hidden) {
07821                         // image is not displayed
07822                         return $info['i'];
07823                 }
07824                 $xkimg = $ximg * $this->k;
07825                 if (!$alt) {
07826                         // only non-alternative immages will be set
07827                         $this->_out(sprintf('q %.2F 0 0 %.2F %.2F %.2F cm /I%u Do Q', ($w * $this->k), ($h * $this->k), $xkimg, (($this->h - ($y + $h)) * $this->k), $info['i']));
07828                 }
07829                 if (!empty($border)) {
07830                         $bx = $this->x;
07831                         $by = $this->y;
07832                         $this->x = $ximg;
07833                         if ($this->rtl) {
07834                                 $this->x += $w;
07835                         }
07836                         $this->y = $y;
07837                         $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
07838                         $this->x = $bx;
07839                         $this->y = $by;
07840                 }
07841                 if ($link) {
07842                         $this->Link($ximg, $y, $w, $h, $link, 0);
07843                 }
07844                 // set pointer to align the next text/objects
07845                 switch($align) {
07846                         case 'T': {
07847                                 $this->y = $y;
07848                                 $this->x = $this->img_rb_x;
07849                                 break;
07850                         }
07851                         case 'M': {
07852                                 $this->y = $y + round($h/2);
07853                                 $this->x = $this->img_rb_x;
07854                                 break;
07855                         }
07856                         case 'B': {
07857                                 $this->y = $this->img_rb_y;
07858                                 $this->x = $this->img_rb_x;
07859                                 break;
07860                         }
07861                         case 'N': {
07862                                 $this->SetY($this->img_rb_y);
07863                                 break;
07864                         }
07865                         default:{
07866                                 break;
07867                         }
07868                 }
07869                 $this->endlinex = $this->img_rb_x;
07870                 if ($this->inxobj) {
07871                         // we are inside an XObject template
07872                         $this->xobjects[$this->xobjid]['images'][] = $info['i'];
07873                 }
07874                 return $info['i'];
07875         }
07876 
07882         public function set_mqr($mqr) {
07883                 if (!defined('PHP_VERSION_ID')) {
07884                         $version = PHP_VERSION;
07885                         define('PHP_VERSION_ID', (($version{0} * 10000) + ($version{2} * 100) + $version{4}));
07886                 }
07887                 if (PHP_VERSION_ID < 50300) {
07888                         @set_magic_quotes_runtime($mqr);
07889                 }
07890         }
07891 
07897         public function get_mqr() {
07898                 if (!defined('PHP_VERSION_ID')) {
07899                         $version = PHP_VERSION;
07900                         define('PHP_VERSION_ID', (($version{0} * 10000) + ($version{2} * 100) + $version{4}));
07901                 }
07902                 if (PHP_VERSION_ID < 50300) {
07903                         return @get_magic_quotes_runtime();
07904                 }
07905                 return 0;
07906         }
07907 
07915         protected function _toJPEG($image) {
07916                 $tempname = tempnam(K_PATH_CACHE, 'jpg_');
07917                 imagejpeg($image, $tempname, $this->jpeg_quality);
07918                 imagedestroy($image);
07919                 $retvars = $this->_parsejpeg($tempname);
07920                 // tidy up by removing temporary image
07921                 unlink($tempname);
07922                 return $retvars;
07923         }
07924 
07933         protected function _toPNG($image) {
07934                 // set temporary image file name
07935                 $tempname = tempnam(K_PATH_CACHE, 'jpg_');
07936                 // turn off interlaced mode
07937                 imageinterlace($image, 0);
07938                 // create temporary PNG image
07939                 imagepng($image, $tempname);
07940                 // remove image from memory
07941                 imagedestroy($image);
07942                 // get PNG image data
07943                 $retvars = $this->_parsepng($tempname);
07944                 // tidy up by removing temporary image
07945                 unlink($tempname);
07946                 return $retvars;
07947         }
07948 
07957         protected function _setGDImageTransparency($new_image, $image) {
07958                 // transparency index
07959                 $tid = imagecolortransparent($image);
07960                 // default transparency color
07961                 $tcol = array('red' => 255, 'green' => 255, 'blue' => 255);
07962                 if ($tid >= 0) {
07963                         // get the colors for the transparency index
07964                         $tcol = imagecolorsforindex($image, $tid);
07965                 }
07966                 $tid = imagecolorallocate($new_image, $tcol['red'], $tcol['green'], $tcol['blue']);
07967                 imagefill($new_image, 0, 0, $tid);
07968                 imagecolortransparent($new_image, $tid);
07969                 return $new_image;
07970         }
07971 
07978         protected function _parsejpeg($file) {
07979                 $a = getimagesize($file);
07980                 if (empty($a)) {
07981                         $this->Error('Missing or incorrect image file: '.$file);
07982                 }
07983                 if ($a[2] != 2) {
07984                         $this->Error('Not a JPEG file: '.$file);
07985                 }
07986                 // bits per pixel
07987                 $bpc = isset($a['bits']) ? intval($a['bits']) : 8;
07988                 // number of image channels
07989                 if (!isset($a['channels'])) {
07990                         $channels = 3;
07991                 } else {
07992                         $channels = intval($a['channels']);
07993                 }
07994                 // default colour space
07995                 switch ($channels) {
07996                         case 1: {
07997                                 $colspace = 'DeviceGray';
07998                                 break;
07999                         }
08000                         case 3: {
08001                                 $colspace = 'DeviceRGB';
08002                                 break;
08003                         }
08004                         case 4: {
08005                                 $colspace = 'DeviceCMYK';
08006                                 break;
08007                         }
08008                         default: {
08009                                 $channels = 3;
08010                                 $colspace = 'DeviceRGB';
08011                                 break;
08012                         }
08013                 }
08014                 // get file content
08015                 $data = file_get_contents($file);
08016                 // check for embedded ICC profile
08017                 $icc = array();
08018                 $offset = 0;
08019                 while (($pos = strpos($data, "ICC_PROFILE\0", $offset)) !== false) {
08020                         // get ICC sequence length
08021                         $length = ($this->_getUSHORT($data, ($pos - 2)) - 16);
08022                         // marker sequence number
08023                         $msn = max(1, ord($data{($pos + 12)}));
08024                         // number of markers (total of APP2 used)
08025                         $nom = max(1, ord($data{($pos + 13)}));
08026                         // get sequence segment
08027                         $icc[($msn - 1)] = substr($data, ($pos + 14), $length);
08028                         // move forward to next sequence
08029                         $offset = ($pos + 14 + $length);
08030                 }
08031                 // order and compact ICC segments
08032                 if (count($icc) > 0) {
08033                         ksort($icc);
08034                         $icc = implode('', $icc);
08035                         if ((ord($icc{36}) != 0x61) OR (ord($icc{37}) != 0x63) OR (ord($icc{38}) != 0x73) OR (ord($icc{39}) != 0x70)) {
08036                                 // invalid ICC profile
08037                                 $icc = false;
08038                         }
08039                 } else {
08040                         $icc = false;
08041                 }
08042                 return array('w' => $a[0], 'h' => $a[1], 'ch' => $channels, 'icc' => $icc, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data);
08043         }
08044 
08051         protected function _parsepng($file) {
08052                 $f = fopen($file, 'rb');
08053                 if ($f === false) {
08054                         $this->Error('Can\'t open image file: '.$file);
08055                 }
08056                 //Check signature
08057                 if (fread($f, 8) != chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) {
08058                         $this->Error('Not a PNG file: '.$file);
08059                 }
08060                 //Read header chunk
08061                 fread($f, 4);
08062                 if (fread($f, 4) != 'IHDR') {
08063                         $this->Error('Incorrect PNG file: '.$file);
08064                 }
08065                 $w = $this->_freadint($f);
08066                 $h = $this->_freadint($f);
08067                 $bpc = ord(fread($f, 1));
08068                 if ($bpc > 8) {
08069                         //$this->Error('16-bit depth not supported: '.$file);
08070                         fclose($f);
08071                         return false;
08072                 }
08073                 $ct = ord(fread($f, 1));
08074                 if ($ct == 0) {
08075                         $colspace = 'DeviceGray';
08076                 } elseif ($ct == 2) {
08077                         $colspace = 'DeviceRGB';
08078                 } elseif ($ct == 3) {
08079                         $colspace = 'Indexed';
08080                 } else {
08081                         // alpha channel
08082                         fclose($f);
08083                         return 'pngalpha';
08084                 }
08085                 if (ord(fread($f, 1)) != 0) {
08086                         //$this->Error('Unknown compression method: '.$file);
08087                         fclose($f);
08088                         return false;
08089                 }
08090                 if (ord(fread($f, 1)) != 0) {
08091                         //$this->Error('Unknown filter method: '.$file);
08092                         fclose($f);
08093                         return false;
08094                 }
08095                 if (ord(fread($f, 1)) != 0) {
08096                         //$this->Error('Interlacing not supported: '.$file);
08097                         fclose($f);
08098                         return false;
08099                 }
08100                 fread($f, 4);
08101                 $channels = ($ct == 2 ? 3 : 1);
08102                 $parms = '/DecodeParms << /Predictor 15 /Colors '.$channels.' /BitsPerComponent '.$bpc.' /Columns '.$w.' >>';
08103                 //Scan chunks looking for palette, transparency and image data
08104                 $pal = '';
08105                 $trns = '';
08106                 $data = '';
08107                 $icc = false;
08108                 do {
08109                         $n = $this->_freadint($f);
08110                         $type = fread($f, 4);
08111                         if ($type == 'PLTE') {
08112                                 // read palette
08113                                 $pal = $this->rfread($f, $n);
08114                                 fread($f, 4);
08115                         } elseif ($type == 'tRNS') {
08116                                 // read transparency info
08117                                 $t = $this->rfread($f, $n);
08118                                 if ($ct == 0) {
08119                                         $trns = array(ord($t{1}));
08120                                 } elseif ($ct == 2) {
08121                                         $trns = array(ord($t{1}), ord($t{3}), ord($t{5}));
08122                                 } else {
08123                                         $pos = strpos($t, chr(0));
08124                                         if ($pos !== false) {
08125                                                 $trns = array($pos);
08126                                         }
08127                                 }
08128                                 fread($f, 4);
08129                         } elseif ($type == 'IDAT') {
08130                                 // read image data block
08131                                 $data .= $this->rfread($f, $n);
08132                                 fread($f, 4);
08133                         } elseif ($type == 'iCCP') {
08134                                 // skip profile name and null separator
08135                                 $len = 0;
08136                                 while ((ord(fread($f, 1)) > 0) AND ($len < 80)) {
08137                                         ++$len;
08138                                 }
08139                                 // get compression method
08140                                 if (ord(fread($f, 1)) != 0) {
08141                                         //$this->Error('Unknown filter method: '.$file);
08142                                         fclose($f);
08143                                         return false;
08144                                 }
08145                                 // read ICC Color Profile
08146                                 $icc = $this->rfread($f, ($n - $len - 2));
08147                                 // decompress profile
08148                                 $icc = gzuncompress($icc);
08149                                 fread($f, 4);
08150                         } elseif ($type == 'IEND') {
08151                                 break;
08152                         } else {
08153                                 $this->rfread($f, $n + 4);
08154                         }
08155                 } while ($n);
08156                 if (($colspace == 'Indexed') AND (empty($pal))) {
08157                         //$this->Error('Missing palette in '.$file);
08158                         fclose($f);
08159                         return false;
08160                 }
08161                 fclose($f);
08162                 return array('w' => $w, 'h' => $h, 'ch' => $channels, 'icc' => $icc, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'parms' => $parms, 'pal' => $pal, 'trns' => $trns, 'data' => $data);
08163         }
08164 
08175         protected function rfread($handle, $length) {
08176                 $data = fread($handle, $length);
08177                 if ($data === false) {
08178                         return false;
08179                 }
08180                 $rest = $length - strlen($data);
08181                 if ($rest > 0) {
08182                         $data .= $this->rfread($handle, $rest);
08183                 }
08184                 return $data;
08185         }
08186 
08208         protected function ImagePngAlpha($file, $x, $y, $wpx, $hpx, $w, $h, $type, $link, $align, $resize, $dpi, $palign, $filehash='') {
08209                 if (empty($filehash)) {
08210                         $filehash = md5($file);
08211                 }
08212                 // create temp image file (without alpha channel)
08213                 $tempfile_plain = K_PATH_CACHE.'mskp_'.$filehash;
08214                 // create temp alpha file
08215                 $tempfile_alpha = K_PATH_CACHE.'mska_'.$filehash;
08216                 if (extension_loaded('imagick')) { // ImageMagick extension
08217                         // ImageMagick library
08218                         $img = new Imagick();
08219                         $img->readImage($file);
08220                         // clone image object
08221                         $imga = $img->clone();
08222                         // extract alpha channel
08223                         $img->separateImageChannel(8); // 8 = (imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE);
08224                         $img->negateImage(true);
08225                         $img->setImageFormat('png');
08226                         $img->writeImage($tempfile_alpha);
08227                         // remove alpha channel
08228                         $imga->separateImageChannel(39); // 39 = (imagick::CHANNEL_ALL & ~(imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE));
08229                         $imga->setImageFormat('png');
08230                         $imga->writeImage($tempfile_plain);
08231                 } elseif (function_exists('imagecreatefrompng')) { // GD extension
08232                         // generate images
08233                         $img = imagecreatefrompng($file);
08234                         $imgalpha = imagecreate($wpx, $hpx);
08235                         // generate gray scale palette (0 -> 255)
08236                         for ($c = 0; $c < 256; ++$c) {
08237                                 ImageColorAllocate($imgalpha, $c, $c, $c);
08238                         }
08239                         // extract alpha channel
08240                         for ($xpx = 0; $xpx < $wpx; ++$xpx) {
08241                                 for ($ypx = 0; $ypx < $hpx; ++$ypx) {
08242                                         $color = imagecolorat($img, $xpx, $ypx);
08243                                         $alpha = ($color >> 24); // shifts off the first 24 bits (where 8x3 are used for each color), and returns the remaining 7 allocated bits (commonly used for alpha)
08244                                         $alpha = (((127 - $alpha) / 127) * 255); // GD alpha is only 7 bit (0 -> 127)
08245                                         $alpha = $this->getGDgamma($alpha); // correct gamma
08246                                         imagesetpixel($imgalpha, $xpx, $ypx, $alpha);
08247                                 }
08248                         }
08249                         imagepng($imgalpha, $tempfile_alpha);
08250                         imagedestroy($imgalpha);
08251                         // extract image without alpha channel
08252                         $imgplain = imagecreatetruecolor($wpx, $hpx);
08253                         imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
08254                         imagepng($imgplain, $tempfile_plain);
08255                         imagedestroy($imgplain);
08256                 } else {
08257                         $this->Error('TCPDF requires the Imagick or GD extension to handle PNG images with alpha channel.');
08258                 }
08259                 // embed mask image
08260                 $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
08261                 // embed image, masked with previously embedded mask
08262                 $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
08263                 // remove temp files
08264                 unlink($tempfile_alpha);
08265                 unlink($tempfile_plain);
08266         }
08267 
08274         protected function getGDgamma($v) {
08275                 return (pow(($v / 255), 2.2) * 255);
08276         }
08277 
08287         public function Ln($h='', $cell=false) {
08288                 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'])) {
08289                         // revove vertical space from the top of the column
08290                         return;
08291                 }
08292                 if ($cell) {
08293                         if ($this->rtl) {
08294                                 $cellpadding = $this->cell_padding['R'];
08295                         } else {
08296                                 $cellpadding = $this->cell_padding['L'];
08297                         }
08298                 } else {
08299                         $cellpadding = 0;
08300                 }
08301                 if ($this->rtl) {
08302                         $this->x = $this->w - $this->rMargin - $cellpadding;
08303                 } else {
08304                         $this->x = $this->lMargin + $cellpadding;
08305                 }
08306                 if (is_string($h)) {
08307                         $this->y += $this->lasth;
08308                 } else {
08309                         $this->y += $h;
08310                 }
08311                 $this->newline = true;
08312         }
08313 
08322         public function GetX() {
08323                 //Get x position
08324                 if ($this->rtl) {
08325                         return ($this->w - $this->x);
08326                 } else {
08327                         return $this->x;
08328                 }
08329         }
08330 
08338         public function GetAbsX() {
08339                 return $this->x;
08340         }
08341 
08349         public function GetY() {
08350                 return $this->y;
08351         }
08352 
08362         public function SetX($x, $rtloff=false) {
08363                 if (!$rtloff AND $this->rtl) {
08364                         if ($x >= 0) {
08365                                 $this->x = $this->w - $x;
08366                         } else {
08367                                 $this->x = abs($x);
08368                         }
08369                 } else {
08370                         if ($x >= 0) {
08371                                 $this->x = $x;
08372                         } else {
08373                                 $this->x = $this->w + $x;
08374                         }
08375                 }
08376                 if ($this->x < 0) {
08377                         $this->x = 0;
08378                 }
08379                 if ($this->x > $this->w) {
08380                         $this->x = $this->w;
08381                 }
08382         }
08383 
08394         public function SetY($y, $resetx=true, $rtloff=false) {
08395                 if ($resetx) {
08396                         //reset x
08397                         if (!$rtloff AND $this->rtl) {
08398                                 $this->x = $this->w - $this->rMargin;
08399                         } else {
08400                                 $this->x = $this->lMargin;
08401                         }
08402                 }
08403                 if ($y >= 0) {
08404                         $this->y = $y;
08405                 } else {
08406                         $this->y = $this->h + $y;
08407                 }
08408                 if ($this->y < 0) {
08409                         $this->y = 0;
08410                 }
08411                 if ($this->y > $this->h) {
08412                         $this->y = $this->h;
08413                 }
08414         }
08415 
08426         public function SetXY($x, $y, $rtloff=false) {
08427                 $this->SetY($y, false, $rtloff);
08428                 $this->SetX($x, $rtloff);
08429         }
08430 
08438         protected function sendOutputData($data, $length) {
08439                 if (!isset($_SERVER['HTTP_ACCEPT_ENCODING']) OR empty($_SERVER['HTTP_ACCEPT_ENCODING'])) {
08440                         // the content length may vary if the server is using compression
08441                         header('Content-Length: '.$length);
08442                 }
08443                 echo $data;
08444         }
08445 
08456         public function Output($name='doc.pdf', $dest='I') {
08457                 //Output PDF to some destination
08458                 //Finish document if necessary
08459                 if ($this->state < 3) {
08460                         $this->Close();
08461                 }
08462                 //Normalize parameters
08463                 if (is_bool($dest)) {
08464                         $dest = $dest ? 'D' : 'F';
08465                 }
08466                 $dest = strtoupper($dest);
08467                 if ($dest{0} != 'F') {
08468                         $name = preg_replace('/[\s]+/', '_', $name);
08469                         $name = preg_replace('/[^a-zA-Z0-9_\.-]/', '', $name);
08470                 }
08471                 if ($this->sign) {
08472                         // *** apply digital signature to the document ***
08473                         // get the document content
08474                         $pdfdoc = $this->getBuffer();
08475                         // remove last newline
08476                         $pdfdoc = substr($pdfdoc, 0, -1);
08477                         // Remove the original buffer
08478                         if (isset($this->diskcache) AND $this->diskcache) {
08479                                 // remove buffer file from cache
08480                                 unlink($this->buffer);
08481                         }
08482                         unset($this->buffer);
08483                         // remove filler space
08484                         $byterange_string_len = strlen($this->byterange_string);
08485                         // define the ByteRange
08486                         $byte_range = array();
08487                         $byte_range[0] = 0;
08488                         $byte_range[1] = strpos($pdfdoc, $this->byterange_string) + $byterange_string_len + 10;
08489                         $byte_range[2] = $byte_range[1] + $this->signature_max_length + 2;
08490                         $byte_range[3] = strlen($pdfdoc) - $byte_range[2];
08491                         $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).substr($pdfdoc, $byte_range[2]);
08492                         // replace the ByteRange
08493                         $byterange = sprintf('/ByteRange[0 %u %u %u]', $byte_range[1], $byte_range[2], $byte_range[3]);
08494                         $byterange .= str_repeat(' ', ($byterange_string_len - strlen($byterange)));
08495                         $pdfdoc = str_replace($this->byterange_string, $byterange, $pdfdoc);
08496                         // write the document to a temporary folder
08497                         $tempdoc = tempnam(K_PATH_CACHE, 'tmppdf_');
08498                         $f = fopen($tempdoc, 'wb');
08499                         if (!$f) {
08500                                 $this->Error('Unable to create temporary file: '.$tempdoc);
08501                         }
08502                         $pdfdoc_length = strlen($pdfdoc);
08503                         fwrite($f, $pdfdoc, $pdfdoc_length);
08504                         fclose($f);
08505                         // get digital signature via openssl library
08506                         $tempsign = tempnam(K_PATH_CACHE, 'tmpsig_');
08507                         if (empty($this->signature_data['extracerts'])) {
08508                                 openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED);
08509                         } else {
08510                                 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']);
08511                         }
08512                         unlink($tempdoc);
08513                         // read signature
08514                         $signature = file_get_contents($tempsign);
08515                         unlink($tempsign);
08516                         // extract signature
08517                         $signature = substr($signature, $pdfdoc_length);
08518                         $signature = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13));
08519                         $tmparr = explode("\n\n", $signature);
08520                         $signature = $tmparr[1];
08521                         unset($tmparr);
08522                         // decode signature
08523                         $signature = base64_decode(trim($signature));
08524                         // convert signature to hex
08525                         $signature = current(unpack('H*', $signature));
08526                         $signature = str_pad($signature, $this->signature_max_length, '0');
08527                         // disable disk caching
08528                         $this->diskcache = false;
08529                         // Add signature to the document
08530                         $this->buffer = substr($pdfdoc, 0, $byte_range[1]).'<'.$signature.'>'.substr($pdfdoc, $byte_range[1]);
08531                         $this->bufferlen = strlen($this->buffer);
08532                 }
08533                 switch($dest) {
08534                         case 'I': {
08535                                 // Send PDF to the standard output
08536                                 if (ob_get_contents()) {
08537                                         $this->Error('Some data has already been output, can\'t send PDF file');
08538                                 }
08539                                 if (php_sapi_name() != 'cli') {
08540                                         // send output to a browser
08541                                         header('Content-Type: application/pdf');
08542                                         if (headers_sent()) {
08543                                                 $this->Error('Some data has already been output to browser, can\'t send PDF file');
08544                                         }
08545                                         header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
08546                                         header('Pragma: public');
08547                                         header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
08548                                         header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
08549                                         header('Content-Disposition: inline; filename="'.basename($name).'";');
08550                                         $this->sendOutputData($this->getBuffer(), $this->bufferlen);
08551                                 } else {
08552                                         echo $this->getBuffer();
08553                                 }
08554                                 break;
08555                         }
08556                         case 'D': {
08557                                 // download PDF as file
08558                                 if (ob_get_contents()) {
08559                                         $this->Error('Some data has already been output, can\'t send PDF file');
08560                                 }
08561                                 header('Content-Description: File Transfer');
08562                                 if (headers_sent()) {
08563                                         $this->Error('Some data has already been output to browser, can\'t send PDF file');
08564                                 }
08565                                 header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
08566                                 header('Pragma: public');
08567                                 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
08568                                 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
08569                                 // force download dialog
08570                                 if (strpos(php_sapi_name(), 'cgi') === false) {
08571                                         header('Content-Type: application/force-download');
08572                                         header('Content-Type: application/octet-stream', false);
08573                                         header('Content-Type: application/download', false);
08574                                         header('Content-Type: application/pdf', false);
08575                                 } else {
08576                                         header('Content-Type: application/pdf');
08577                                 }
08578                                 // use the Content-Disposition header to supply a recommended filename
08579                                 header('Content-Disposition: attachment; filename="'.basename($name).'";');
08580                                 header('Content-Transfer-Encoding: binary');
08581                                 $this->sendOutputData($this->getBuffer(), $this->bufferlen);
08582                                 break;
08583                         }
08584                         case 'F':
08585                         case 'FI':
08586                         case 'FD': {
08587                                 // save PDF to a local file
08588                                 if ($this->diskcache) {
08589                                         copy($this->buffer, $name);
08590                                 } else {
08591                                         $f = fopen($name, 'wb');
08592                                         if (!$f) {
08593                                                 $this->Error('Unable to create output file: '.$name);
08594                                         }
08595                                         fwrite($f, $this->getBuffer(), $this->bufferlen);
08596                                         fclose($f);
08597                                 }
08598                                 if ($dest == 'FI') {
08599                                         // send headers to browser
08600                                         header('Content-Type: application/pdf');
08601                                         header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
08602                                         header('Pragma: public');
08603                                         header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
08604                                         header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
08605                                         header('Content-Disposition: inline; filename="'.basename($name).'";');
08606                                         $this->sendOutputData(file_get_contents($name), filesize($name));
08607                                 } elseif ($dest == 'FD') {
08608                                         // send headers to browser
08609                                         if (ob_get_contents()) {
08610                                                 $this->Error('Some data has already been output, can\'t send PDF file');
08611                                         }
08612                                         header('Content-Description: File Transfer');
08613                                         if (headers_sent()) {
08614                                                 $this->Error('Some data has already been output to browser, can\'t send PDF file');
08615                                         }
08616                                         header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
08617                                         header('Pragma: public');
08618                                         header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
08619                                         header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
08620                                         // force download dialog
08621                                         if (strpos(php_sapi_name(), 'cgi') === false) {
08622                                                 header('Content-Type: application/force-download');
08623                                                 header('Content-Type: application/octet-stream', false);
08624                                                 header('Content-Type: application/download', false);
08625                                                 header('Content-Type: application/pdf', false);
08626                                         } else {
08627                                                 header('Content-Type: application/pdf');
08628                                         }
08629                                         // use the Content-Disposition header to supply a recommended filename
08630                                         header('Content-Disposition: attachment; filename="'.basename($name).'";');
08631                                         header('Content-Transfer-Encoding: binary');
08632                                         $this->sendOutputData(file_get_contents($name), filesize($name));
08633                                 }
08634                                 break;
08635                         }
08636                         case 'E': {
08637                                 // return PDF as base64 mime multi-part email attachment (RFC 2045)
08638                                 $retval = 'Content-Type: application/pdf;'."\r\n";
08639                                 $retval .= ' name="'.$name.'"'."\r\n";
08640                                 $retval .= 'Content-Transfer-Encoding: base64'."\r\n";
08641                                 $retval .= 'Content-Disposition: attachment;'."\r\n";
08642                                 $retval .= ' filename="'.$name.'"'."\r\n\r\n";
08643                                 $retval .= chunk_split(base64_encode($this->getBuffer()), 76, "\r\n");
08644                                 return $retval;
08645                         }
08646                         case 'S': {
08647                                 // returns PDF as a string
08648                                 return $this->getBuffer();
08649                         }
08650                         default: {
08651                                 $this->Error('Incorrect output destination: '.$dest);
08652                         }
08653                 }
08654                 return '';
08655         }
08656 
08664         public function _destroy($destroyall=false, $preserve_objcopy=false) {
08665                 if ($destroyall AND isset($this->diskcache) AND $this->diskcache AND (!$preserve_objcopy) AND (!$this->empty_string($this->buffer))) {
08666                         // remove buffer file from cache
08667                         unlink($this->buffer);
08668                 }
08669                 foreach (array_keys(get_object_vars($this)) as $val) {
08670                         if ($destroyall OR (
08671                                 ($val != 'internal_encoding')
08672                                 AND ($val != 'state')
08673                                 AND ($val != 'bufferlen')
08674                                 AND ($val != 'buffer')
08675                                 AND ($val != 'diskcache')
08676                                 AND ($val != 'sign')
08677                                 AND ($val != 'signature_data')
08678                                 AND ($val != 'signature_max_length')
08679                                 AND ($val != 'byterange_string')
08680                                 )) {
08681                                 if ((!$preserve_objcopy OR ($val != 'objcopy')) AND isset($this->$val)) {
08682                                         unset($this->$val);
08683                                 }
08684                         }
08685                 }
08686         }
08687 
08692         protected function _dochecks() {
08693                 //Check for locale-related bug
08694                 if (1.1 == 1) {
08695                         $this->Error('Don\'t alter the locale before including class file');
08696                 }
08697                 //Check for decimal separator
08698                 if (sprintf('%.1F', 1.0) != '1.0') {
08699                         setlocale(LC_NUMERIC, 'C');
08700                 }
08701         }
08702 
08708         protected function _getfontpath() {
08709                 if (!defined('K_PATH_FONTS') AND is_dir(dirname(__FILE__).'/fonts')) {
08710                         define('K_PATH_FONTS', dirname(__FILE__).'/fonts/');
08711                 }
08712                 return defined('K_PATH_FONTS') ? K_PATH_FONTS : '';
08713         }
08714 
08721         protected function getInternalPageNumberAliases($a= '') {
08722                 $alias = array();
08723                 // build array of Unicode + ASCII variants (the order is important)
08724                 $alias = array('u' => array(), 'a' => array());
08725                 $u = '{'.$a.'}';
08726                 $alias['u'][] = $this->_escape($u);
08727                 if ($this->isunicode) {
08728                         $alias['u'][] = $this->_escape($this->UTF8ToLatin1($u));
08729                         $alias['u'][] = $this->_escape($this->utf8StrRev($u, false, $this->tmprtl));
08730                         $alias['a'][] = $this->_escape($this->UTF8ToLatin1($a));
08731                         $alias['a'][] = $this->_escape($this->utf8StrRev($a, false, $this->tmprtl));
08732                 }
08733                 $alias['a'][] = $this->_escape($a);
08734                 return $alias;
08735         }
08736 
08742         protected function getAllInternalPageNumberAliases() {
08743                 $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);
08744                 $pnalias = array();
08745                 foreach($basic_alias as $k => $a) {
08746                         $pnalias[$k] = $this->getInternalPageNumberAliases($a);
08747                 }
08748                 return $pnalias;
08749         }
08750 
08759         protected function replacePageNumAliases($page, $replace, $diff=0) {
08760                 foreach ($replace as $rep) {
08761                         foreach ($rep[3] as $a) {
08762                                 if (strpos($page, $a) !== false) {
08763                                         $page = str_replace($a, $rep[0], $page);
08764                                         $diff += ($rep[2] - $rep[1]);
08765                                 }
08766                         }
08767                 }
08768                 return array($page, $diff);
08769         }
08770 
08780         protected function replaceRightShiftPageNumAliases($page, $aliases, $diff) {
08781                 foreach ($aliases as $type => $alias) {
08782                         foreach ($alias as $a) {
08783                                 // find position of compensation factor
08784                                 $startnum = (strpos($a, ':') + 1);
08785                                 $a = substr($a, 0, $startnum);
08786                                 if (($pos = strpos($page, $a)) !== false) {
08787                                         // end of alias
08788                                         $endnum = strpos($page, '}', $pos);
08789                                         // string to be replaced
08790                                         $aa = substr($page, $pos, ($endnum - $pos + 1));
08791                                         // get compensation factor
08792                                         $ratio = substr($page, ($pos + $startnum), ($endnum - $pos - $startnum));
08793                                         $ratio = preg_replace('/[^0-9\.]/', '', $ratio);
08794                                         $ratio = floatval($ratio);
08795                                         if ($type == 'u') {
08796                                                 $chrdiff = floor(($diff + 12) * $ratio);
08797                                                 $shift = str_repeat(' ', $chrdiff);
08798                                                 $shift = $this->UTF8ToUTF16BE($shift, false);
08799                                         } else {
08800                                                 $chrdiff = floor(($diff + 11) * $ratio);
08801                                                 $shift = str_repeat(' ', $chrdiff);
08802                                         }
08803                                         $page = str_replace($aa, $shift, $page);
08804                                 }
08805                         }
08806                 }
08807                 return $page;
08808         }
08809 
08814         protected function _putpages() {
08815                 $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
08816                 // get internal aliases for page numbers
08817                 $pnalias = $this->getAllInternalPageNumberAliases();
08818                 $num_pages = $this->numpages;
08819                 $ptpa = $this->formatPageNumber(($this->starting_page_number + $num_pages - 1));
08820                 $ptpu = $this->UTF8ToUTF16BE($ptpa, false);
08821                 $ptp_num_chars = $this->GetNumChars($ptpa);
08822                 $pagegroupnum = 0;
08823                 $groupnum = 0;
08824                 $ptgu = 1;
08825                 $ptga = 1;
08826                 for ($n = 1; $n <= $num_pages; ++$n) {
08827                         // get current page
08828                         $temppage = $this->getPageBuffer($n);
08829                         $pagelen = strlen($temppage);
08830                         // set replacements for total pages number
08831                         $pnpa = $this->formatPageNumber(($this->starting_page_number + $n - 1));
08832                         $pnpu = $this->UTF8ToUTF16BE($pnpa, false);
08833                         $pnp_num_chars = $this->GetNumChars($pnpa);
08834                         $pdiff = 0; // difference used for right shift alignment of page numbers
08835                         $gdiff = 0; // difference used for right shift alignment of page group numbers
08836                         if (!empty($this->pagegroups)) {
08837                                 if (isset($this->newpagegroup[$n])) {
08838                                         $pagegroupnum = 0;
08839                                         ++$groupnum;
08840                                         $ptga = $this->formatPageNumber($this->pagegroups[$groupnum]);
08841                                         $ptgu = $this->UTF8ToUTF16BE($ptga, false);
08842                                         $ptg_num_chars = $this->GetNumChars($ptga);
08843                                 }
08844                                 ++$pagegroupnum;
08845                                 $pnga = $this->formatPageNumber($pagegroupnum);
08846                                 $pngu = $this->UTF8ToUTF16BE($pnga, false);
08847                                 $png_num_chars = $this->GetNumChars($pnga);
08848                                 // replace page numbers
08849                                 $replace = array();
08850                                 $replace[] = array($ptgu, $ptg_num_chars, 9, $pnalias[2]['u']);
08851                                 $replace[] = array($ptga, $ptg_num_chars, 7, $pnalias[2]['a']);
08852                                 $replace[] = array($pngu, $png_num_chars, 9, $pnalias[3]['u']);
08853                                 $replace[] = array($pnga, $png_num_chars, 7, $pnalias[3]['a']);
08854                                 list($temppage, $gdiff) = $this->replacePageNumAliases($temppage, $replace, $gdiff);
08855                         }
08856                         // replace page numbers
08857                         $replace = array();
08858                         $replace[] = array($ptpu, $ptp_num_chars, 9, $pnalias[0]['u']);
08859                         $replace[] = array($ptpa, $ptp_num_chars, 7, $pnalias[0]['a']);
08860                         $replace[] = array($pnpu, $pnp_num_chars, 9, $pnalias[1]['u']);
08861                         $replace[] = array($pnpa, $pnp_num_chars, 7, $pnalias[1]['a']);
08862                         list($temppage, $pdiff) = $this->replacePageNumAliases($temppage, $replace, $pdiff);
08863                         // replace right shift alias
08864                         $temppage = $this->replaceRightShiftPageNumAliases($temppage, $pnalias[4], max($pdiff, $gdiff));
08865                         // replace EPS marker
08866                         $temppage = str_replace($this->epsmarker, '', $temppage);
08867                         //Page
08868                         $this->page_obj_id[$n] = $this->_newobj();
08869                         $out = '<<';
08870                         $out .= ' /Type /Page';
08871                         $out .= ' /Parent 1 0 R';
08872                         $out .= ' /LastModified '.$this->_datestring();
08873                         $out .= ' /Resources 2 0 R';
08874                         $boxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox');
08875                         foreach ($boxes as $box) {
08876                                 $out .= ' /'.$box;
08877                                 $out .= sprintf(' [%.2F %.2F %.2F %.2F]', $this->pagedim[$n][$box]['llx'], $this->pagedim[$n][$box]['lly'], $this->pagedim[$n][$box]['urx'], $this->pagedim[$n][$box]['ury']);
08878                         }
08879                         if (isset($this->pagedim[$n]['BoxColorInfo']) AND !empty($this->pagedim[$n]['BoxColorInfo'])) {
08880                                 $out .= ' /BoxColorInfo <<';
08881                                 foreach ($boxes as $box) {
08882                                         if (isset($this->pagedim[$n]['BoxColorInfo'][$box])) {
08883                                                 $out .= ' /'.$box.' <<';
08884                                                 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['C'])) {
08885                                                         $color = $this->pagedim[$n]['BoxColorInfo'][$box]['C'];
08886                                                         $out .= ' /C [';
08887                                                         $out .= sprintf(' %.3F %.3F %.3F', $color[0]/255, $color[1]/255, $color[2]/255);
08888                                                         $out .= ' ]';
08889                                                 }
08890                                                 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['W'])) {
08891                                                         $out .= ' /W '.($this->pagedim[$n]['BoxColorInfo'][$box]['W'] * $this->k);
08892                                                 }
08893                                                 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['S'])) {
08894                                                         $out .= ' /S /'.$this->pagedim[$n]['BoxColorInfo'][$box]['S'];
08895                                                 }
08896                                                 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['D'])) {
08897                                                         $dashes = $this->pagedim[$n]['BoxColorInfo'][$box]['D'];
08898                                                         $out .= ' /D [';
08899                                                         foreach ($dashes as $dash) {
08900                                                                 $out .= sprintf(' %.3F', ($dash * $this->k));
08901                                                         }
08902                                                         $out .= ' ]';
08903                                                 }
08904                                                 $out .= ' >>';
08905                                         }
08906                                 }
08907                                 $out .= ' >>';
08908                         }
08909                         $out .= ' /Contents '.($this->n + 1).' 0 R';
08910                         $out .= ' /Rotate '.$this->pagedim[$n]['Rotate'];
08911                         if (!$this->pdfa_mode) {
08912                                 $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceRGB >>';
08913                         }
08914                         if (isset($this->pagedim[$n]['trans']) AND !empty($this->pagedim[$n]['trans'])) {
08915                                 // page transitions
08916                                 if (isset($this->pagedim[$n]['trans']['Dur'])) {
08917                                         $out .= ' /Dur '.$this->pagedim[$n]['trans']['Dur'];
08918                                 }
08919                                 $out .= ' /Trans <<';
08920                                 $out .= ' /Type /Trans';
08921                                 if (isset($this->pagedim[$n]['trans']['S'])) {
08922                                         $out .= ' /S /'.$this->pagedim[$n]['trans']['S'];
08923                                 }
08924                                 if (isset($this->pagedim[$n]['trans']['D'])) {
08925                                         $out .= ' /D '.$this->pagedim[$n]['trans']['D'];
08926                                 }
08927                                 if (isset($this->pagedim[$n]['trans']['Dm'])) {
08928                                         $out .= ' /Dm /'.$this->pagedim[$n]['trans']['Dm'];
08929                                 }
08930                                 if (isset($this->pagedim[$n]['trans']['M'])) {
08931                                         $out .= ' /M /'.$this->pagedim[$n]['trans']['M'];
08932                                 }
08933                                 if (isset($this->pagedim[$n]['trans']['Di'])) {
08934                                         $out .= ' /Di '.$this->pagedim[$n]['trans']['Di'];
08935                                 }
08936                                 if (isset($this->pagedim[$n]['trans']['SS'])) {
08937                                         $out .= ' /SS '.$this->pagedim[$n]['trans']['SS'];
08938                                 }
08939                                 if (isset($this->pagedim[$n]['trans']['B'])) {
08940                                         $out .= ' /B '.$this->pagedim[$n]['trans']['B'];
08941                                 }
08942                                 $out .= ' >>';
08943                         }
08944                         $out .= $this->_getannotsrefs($n);
08945                         $out .= ' /PZ '.$this->pagedim[$n]['PZ'];
08946                         $out .= ' >>';
08947                         $out .= "\n".'endobj';
08948                         $this->_out($out);
08949                         //Page content
08950                         $p = ($this->compress) ? gzcompress($temppage) : $temppage;
08951                         $this->_newobj();
08952                         $p = $this->_getrawstream($p);
08953                         $this->_out('<<'.$filter.'/Length '.strlen($p).'>> stream'."\n".$p."\n".'endstream'."\n".'endobj');
08954                         if ($this->diskcache) {
08955                                 // remove temporary files
08956                                 unlink($this->pages[$n]);
08957                         }
08958                 }
08959                 //Pages root
08960                 $out = $this->_getobj(1)."\n";
08961                 $out .= '<< /Type /Pages /Kids [';
08962                 foreach($this->page_obj_id as $page_obj) {
08963                         $out .= ' '.$page_obj.' 0 R';
08964                 }
08965                 $out .= ' ] /Count '.$num_pages.' >>';
08966                 $out .= "\n".'endobj';
08967                 $this->_out($out);
08968         }
08969 
08978         protected function _putannotsrefs($n) {
08979                 $this->_out($this->_getannotsrefs($n));
08980         }
08981 
08990         protected function _getannotsrefs($n) {
08991                 if (!(isset($this->PageAnnots[$n]) OR ($this->sign AND isset($this->signature_data['cert_type'])))) {
08992                         return '';
08993                 }
08994                 $out = ' /Annots [';
08995                 if (isset($this->PageAnnots[$n])) {
08996                         foreach ($this->PageAnnots[$n] as $key => $val) {
08997                                 if (!in_array($val['n'], $this->radio_groups)) {
08998                                         $out .= ' '.$val['n'].' 0 R';
08999                                 }
09000                         }
09001                         // add radiobutton groups
09002                         if (isset($this->radiobutton_groups[$n])) {
09003                                 foreach ($this->radiobutton_groups[$n] as $key => $data) {
09004                                         if (isset($data['n'])) {
09005                                                 $out .= ' '.$data['n'].' 0 R';
09006                                         }
09007                                 }
09008                         }
09009                 }
09010                 if ($this->sign AND ($n == $this->signature_appearance['page']) AND isset($this->signature_data['cert_type'])) {
09011                         // set reference for signature object
09012                         $out .= ' '.$this->sig_obj_id.' 0 R';
09013                 }
09014                 if (!empty($this->empty_signature_appearance)) {
09015                         foreach ($this->empty_signature_appearance as $esa) {
09016                                 if ($esa['page'] == $n) {
09017                                         // set reference for empty signature objects
09018                                         $out .= ' '.$esa['objid'].' 0 R';
09019                                 }
09020                         }
09021                 }
09022                 $out .= ' ]';
09023                 return $out;
09024         }
09025 
09034         protected function _putannotsobjs() {
09035                 // reset object counter
09036                 for ($n=1; $n <= $this->numpages; ++$n) {
09037                         if (isset($this->PageAnnots[$n])) {
09038                                 // set page annotations
09039                                 foreach ($this->PageAnnots[$n] as $key => $pl) {
09040                                         $annot_obj_id = $this->PageAnnots[$n][$key]['n'];
09041                                         // create annotation object for grouping radiobuttons
09042                                         if (isset($this->radiobutton_groups[$n][$pl['txt']]) AND is_array($this->radiobutton_groups[$n][$pl['txt']])) {
09043                                                 $radio_button_obj_id = $this->radiobutton_groups[$n][$pl['txt']]['n'];
09044                                                 $annots = '<<';
09045                                                 $annots .= ' /Type /Annot';
09046                                                 $annots .= ' /Subtype /Widget';
09047                                                 $annots .= ' /Rect [0 0 0 0]';
09048                                                 if ($this->radiobutton_groups[$n][$pl['txt']]['#readonly#']) {
09049                                                         // read only
09050                                                         $annots .= ' /F 68';
09051                                                         $annots .= ' /Ff 49153';
09052                                                 } else {
09053                                                         $annots .= ' /F 4'; // default print for PDF/A
09054                                                         $annots .= ' /Ff 49152';
09055                                                 }
09056                                                 $annots .= ' /T '.$this->_datastring($pl['txt'], $radio_button_obj_id);
09057                                                 $annots .= ' /FT /Btn';
09058                                                 $annots .= ' /Kids [';
09059                                                 foreach ($this->radiobutton_groups[$n][$pl['txt']] as $key => $data) {
09060                                                         if (isset($data['kid'])) {
09061                                                                 $annots .= ' '.$data['kid'].' 0 R';
09062                                                                 if ($data['def'] !== 'Off') {
09063                                                                         $defval = $data['def'];
09064                                                                 }
09065                                                         }
09066                                                 }
09067                                                 $annots .= ' ]';
09068                                                 if (isset($defval)) {
09069                                                         $annots .= ' /V /'.$defval;
09070                                                 }
09071                                                 $annots .= ' >>';
09072                                                 $this->_out($this->_getobj($radio_button_obj_id)."\n".$annots."\n".'endobj');
09073                                                 $this->form_obj_id[] = $radio_button_obj_id;
09074                                                 // store object id to be used on Parent entry of Kids
09075                                                 $this->radiobutton_groups[$n][$pl['txt']] = $radio_button_obj_id;
09076                                         }
09077                                         $formfield = false;
09078                                         $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER);
09079                                         $a = $pl['x'] * $this->k;
09080                                         $b = $this->pagedim[$n]['h'] - (($pl['y'] + $pl['h']) * $this->k);
09081                                         $c = $pl['w'] * $this->k;
09082                                         $d = $pl['h'] * $this->k;
09083                                         $rect = sprintf('%.2F %.2F %.2F %.2F', $a, $b, $a+$c, $b+$d);
09084                                         // create new annotation object
09085                                         $annots = '<</Type /Annot';
09086                                         $annots .= ' /Subtype /'.$pl['opt']['subtype'];
09087                                         $annots .= ' /Rect ['.$rect.']';
09088                                         $ft = array('Btn', 'Tx', 'Ch', 'Sig');
09089                                         if (isset($pl['opt']['ft']) AND in_array($pl['opt']['ft'], $ft)) {
09090                                                 $annots .= ' /FT /'.$pl['opt']['ft'];
09091                                                 $formfield = true;
09092                                         }
09093                                         $annots .= ' /Contents '.$this->_textstring($pl['txt'], $annot_obj_id);
09094                                         $annots .= ' /P '.$this->page_obj_id[$n].' 0 R';
09095                                         $annots .= ' /NM '.$this->_datastring(sprintf('%04u-%04u', $n, $key), $annot_obj_id);
09096                                         $annots .= ' /M '.$this->_datestring($annot_obj_id);
09097                                         if (isset($pl['opt']['f'])) {
09098                                                 $fval = 0;
09099                                                 if (is_array($pl['opt']['f'])) {
09100                                                         foreach ($pl['opt']['f'] as $f) {
09101                                                                 switch (strtolower($f)) {
09102                                                                         case 'invisible': {
09103                                                                                 $fval += 1 << 0;
09104                                                                                 break;
09105                                                                         }
09106                                                                         case 'hidden': {
09107                                                                                 $fval += 1 << 1;
09108                                                                                 break;
09109                                                                         }
09110                                                                         case 'print': {
09111                                                                                 $fval += 1 << 2;
09112                                                                                 break;
09113                                                                         }
09114                                                                         case 'nozoom': {
09115                                                                                 $fval += 1 << 3;
09116                                                                                 break;
09117                                                                         }
09118                                                                         case 'norotate': {
09119                                                                                 $fval += 1 << 4;
09120                                                                                 break;
09121                                                                         }
09122                                                                         case 'noview': {
09123                                                                                 $fval += 1 << 5;
09124                                                                                 break;
09125                                                                         }
09126                                                                         case 'readonly': {
09127                                                                                 $fval += 1 << 6;
09128                                                                                 break;
09129                                                                         }
09130                                                                         case 'locked': {
09131                                                                                 $fval += 1 << 8;
09132                                                                                 break;
09133                                                                         }
09134                                                                         case 'togglenoview': {
09135                                                                                 $fval += 1 << 9;
09136                                                                                 break;
09137                                                                         }
09138                                                                         case 'lockedcontents': {
09139                                                                                 $fval += 1 << 10;
09140                                                                                 break;
09141                                                                         }
09142                                                                         default: {
09143                                                                                 break;
09144                                                                         }
09145                                                                 }
09146                                                         }
09147                                                 } else {
09148                                                         $fval = intval($pl['opt']['f']);
09149                                                 }
09150                                         } else {
09151                                                 $fval = 4;
09152                                         }
09153                                         if ($this->pdfa_mode) {
09154                                                 // force print flag for PDF/A mode
09155                                                 $fval |= 4;
09156                                         }
09157                                         $annots .= ' /F '.intval($fval);
09158                                         if (isset($pl['opt']['as']) AND is_string($pl['opt']['as'])) {
09159                                                 $annots .= ' /AS /'.$pl['opt']['as'];
09160                                         }
09161                                         if (isset($pl['opt']['ap'])) {
09162                                                 // appearance stream
09163                                                 $annots .= ' /AP <<';
09164                                                 if (is_array($pl['opt']['ap'])) {
09165                                                         foreach ($pl['opt']['ap'] as $apmode => $apdef) {
09166                                                                 // $apmode can be: n = normal; r = rollover; d = down;
09167                                                                 $annots .= ' /'.strtoupper($apmode);
09168                                                                 if (is_array($apdef)) {
09169                                                                         $annots .= ' <<';
09170                                                                         foreach ($apdef as $apstate => $stream) {
09171                                                                                 // reference to XObject that define the appearance for this mode-state
09172                                                                                 $apsobjid = $this->_putAPXObject($c, $d, $stream);
09173                                                                                 $annots .= ' /'.$apstate.' '.$apsobjid.' 0 R';
09174                                                                         }
09175                                                                         $annots .= ' >>';
09176                                                                 } else {
09177                                                                         // reference to XObject that define the appearance for this mode
09178                                                                         $apsobjid = $this->_putAPXObject($c, $d, $apdef);
09179                                                                         $annots .= ' '.$apsobjid.' 0 R';
09180                                                                 }
09181                                                         }
09182                                                 } else {
09183                                                         $annots .= $pl['opt']['ap'];
09184                                                 }
09185                                                 $annots .= ' >>';
09186                                         }
09187                                         if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) {
09188                                                 $annots .= ' /BS <<';
09189                                                 $annots .= ' /Type /Border';
09190                                                 if (isset($pl['opt']['bs']['w'])) {
09191                                                         $annots .= ' /W '.intval($pl['opt']['bs']['w']);
09192                                                 }
09193                                                 $bstyles = array('S', 'D', 'B', 'I', 'U');
09194                                                 if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $bstyles)) {
09195                                                         $annots .= ' /S /'.$pl['opt']['bs']['s'];
09196                                                 }
09197                                                 if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) {
09198                                                         $annots .= ' /D [';
09199                                                         foreach ($pl['opt']['bs']['d'] as $cord) {
09200                                                                 $annots .= ' '.intval($cord);
09201                                                         }
09202                                                         $annots .= ']';
09203                                                 }
09204                                                 $annots .= ' >>';
09205                                         } else {
09206                                                 $annots .= ' /Border [';
09207                                                 if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) {
09208                                                         $annots .= intval($pl['opt']['border'][0]).' ';
09209                                                         $annots .= intval($pl['opt']['border'][1]).' ';
09210                                                         $annots .= intval($pl['opt']['border'][2]);
09211                                                         if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) {
09212                                                                 $annots .= ' [';
09213                                                                 foreach ($pl['opt']['border'][3] as $dash) {
09214                                                                         $annots .= intval($dash).' ';
09215                                                                 }
09216                                                                 $annots .= ']';
09217                                                         }
09218                                                 } else {
09219                                                         $annots .= '0 0 0';
09220                                                 }
09221                                                 $annots .= ']';
09222                                         }
09223                                         if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) {
09224                                                 $annots .= ' /BE <<';
09225                                                 $bstyles = array('S', 'C');
09226                                                 if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $markups)) {
09227                                                         $annots .= ' /S /'.$pl['opt']['bs']['s'];
09228                                                 } else {
09229                                                         $annots .= ' /S /S';
09230                                                 }
09231                                                 if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) {
09232                                                         $annots .= ' /I '.sprintf(' %.4F', $pl['opt']['be']['i']);
09233                                                 }
09234                                                 $annots .= '>>';
09235                                         }
09236                                         if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c'])) AND !empty($pl['opt']['c'])) {
09237                                                 $annots .= ' /C [';
09238                                                 foreach ($pl['opt']['c'] as $col) {
09239                                                         $col = intval($col);
09240                                                         $color = $col <= 0 ? 0 : ($col >= 255 ? 1 : $col / 255);
09241                                                         $annots .= sprintf(' %.4F', $color);
09242                                                 }
09243                                                 $annots .= ']';
09244                                         }
09245                                         //$annots .= ' /StructParent ';
09246                                         //$annots .= ' /OC ';
09247                                         $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight', 'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound');
09248                                         if (in_array(strtolower($pl['opt']['subtype']), $markups)) {
09249                                                 // this is a markup type
09250                                                 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
09251                                                         $annots .= ' /T '.$this->_textstring($pl['opt']['t'], $annot_obj_id);
09252                                                 }
09253                                                 //$annots .= ' /Popup ';
09254                                                 if (isset($pl['opt']['ca'])) {
09255                                                         $annots .= ' /CA '.sprintf('%.4F', floatval($pl['opt']['ca']));
09256                                                 }
09257                                                 if (isset($pl['opt']['rc'])) {
09258                                                         $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
09259                                                 }
09260                                                 $annots .= ' /CreationDate '.$this->_datestring($annot_obj_id);
09261                                                 //$annots .= ' /IRT ';
09262                                                 if (isset($pl['opt']['subj'])) {
09263                                                         $annots .= ' /Subj '.$this->_textstring($pl['opt']['subj'], $annot_obj_id);
09264                                                 }
09265                                                 //$annots .= ' /RT ';
09266                                                 //$annots .= ' /IT ';
09267                                                 //$annots .= ' /ExData ';
09268                                         }
09269                                         $lineendings = array('Square', 'Circle', 'Diamond', 'OpenArrow', 'ClosedArrow', 'None', 'Butt', 'ROpenArrow', 'RClosedArrow', 'Slash');
09270                                         // Annotation types
09271                                         switch (strtolower($pl['opt']['subtype'])) {
09272                                                 case 'text': {
09273                                                         if (isset($pl['opt']['open'])) {
09274                                                                 $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false');
09275                                                         }
09276                                                         $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph');
09277                                                         if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
09278                                                                 $annots .= ' /Name /'.$pl['opt']['name'];
09279                                                         } else {
09280                                                                 $annots .= ' /Name /Note';
09281                                                         }
09282                                                         $statemodels = array('Marked', 'Review');
09283                                                         if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) {
09284                                                                 $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
09285                                                         } else {
09286                                                                 $pl['opt']['statemodel'] = 'Marked';
09287                                                                 $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
09288                                                         }
09289                                                         if ($pl['opt']['statemodel'] == 'Marked') {
09290                                                                 $states = array('Accepted', 'Unmarked');
09291                                                         } else {
09292                                                                 $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None');
09293                                                         }
09294                                                         if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) {
09295                                                                 $annots .= ' /State /'.$pl['opt']['state'];
09296                                                         } else {
09297                                                                 if ($pl['opt']['statemodel'] == 'Marked') {
09298                                                                         $annots .= ' /State /Unmarked';
09299                                                                 } else {
09300                                                                         $annots .= ' /State /None';
09301                                                                 }
09302                                                         }
09303                                                         break;
09304                                                 }
09305                                                 case 'link': {
09306                                                         if (is_string($pl['txt'])) {
09307                                                                 // external URI link
09308                                                                 $annots .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($pl['txt']), $annot_obj_id).'>>';
09309                                                         } else {
09310                                                                 // internal link
09311                                                                 $l = $this->links[$pl['txt']];
09312                                                                 if (isset($this->page_obj_id[($l[0])])) {
09313                                                                         $annots .= sprintf(' /Dest [%u 0 R /XYZ 0 %.2F null]', $this->page_obj_id[($l[0])], ($this->pagedim[$l[0]]['h'] - ($l[1] * $this->k)));
09314                                                                 }
09315                                                         }
09316                                                         $hmodes = array('N', 'I', 'O', 'P');
09317                                                         if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) {
09318                                                                 $annots .= ' /H /'.$pl['opt']['h'];
09319                                                         } else {
09320                                                                 $annots .= ' /H /I';
09321                                                         }
09322                                                         //$annots .= ' /PA ';
09323                                                         //$annots .= ' /Quadpoints ';
09324                                                         break;
09325                                                 }
09326                                                 case 'freetext': {
09327                                                         if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
09328                                                                 $annots .= ' /DA ('.$pl['opt']['da'].')';
09329                                                         }
09330                                                         if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
09331                                                                 $annots .= ' /Q '.intval($pl['opt']['q']);
09332                                                         }
09333                                                         if (isset($pl['opt']['rc'])) {
09334                                                                 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
09335                                                         }
09336                                                         if (isset($pl['opt']['ds'])) {
09337                                                                 $annots .= ' /DS '.$this->_textstring($pl['opt']['ds'], $annot_obj_id);
09338                                                         }
09339                                                         if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) {
09340                                                                 $annots .= ' /CL [';
09341                                                                 foreach ($pl['opt']['cl'] as $cl) {
09342                                                                         $annots .= sprintf('%.4F ', $cl * $this->k);
09343                                                                 }
09344                                                                 $annots .= ']';
09345                                                         }
09346                                                         $tfit = array('FreeText', 'FreeTextCallout', 'FreeTextTypeWriter');
09347                                                         if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) {
09348                                                                 $annots .= ' /IT /'.$pl['opt']['it'];
09349                                                         }
09350                                                         if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) {
09351                                                                 $l = $pl['opt']['rd'][0] * $this->k;
09352                                                                 $r = $pl['opt']['rd'][1] * $this->k;
09353                                                                 $t = $pl['opt']['rd'][2] * $this->k;
09354                                                                 $b = $pl['opt']['rd'][3] * $this->k;
09355                                                                 $annots .= ' /RD ['.sprintf('%.2F %.2F %.2F %.2F', $l, $r, $t, $b).']';
09356                                                         }
09357                                                         if (isset($pl['opt']['le']) AND in_array($pl['opt']['le'], $lineendings)) {
09358                                                                 $annots .= ' /LE /'.$pl['opt']['le'];
09359                                                         }
09360                                                         break;
09361                                                 }
09362                                                 case 'line': {
09363                                                         break;
09364                                                 }
09365                                                 case 'square': {
09366                                                         break;
09367                                                 }
09368                                                 case 'circle': {
09369                                                         break;
09370                                                 }
09371                                                 case 'polygon': {
09372                                                         break;
09373                                                 }
09374                                                 case 'polyline': {
09375                                                         break;
09376                                                 }
09377                                                 case 'highlight': {
09378                                                         break;
09379                                                 }
09380                                                 case 'underline': {
09381                                                         break;
09382                                                 }
09383                                                 case 'squiggly': {
09384                                                         break;
09385                                                 }
09386                                                 case 'strikeout': {
09387                                                         break;
09388                                                 }
09389                                                 case 'stamp': {
09390                                                         break;
09391                                                 }
09392                                                 case 'caret': {
09393                                                         break;
09394                                                 }
09395                                                 case 'ink': {
09396                                                         break;
09397                                                 }
09398                                                 case 'popup': {
09399                                                         break;
09400                                                 }
09401                                                 case 'fileattachment': {
09402                                                         if ($this->pdfa_mode) {
09403                                                                 // embedded files are not allowed in PDF/A mode
09404                                                                 break;
09405                                                         }
09406                                                         if (!isset($pl['opt']['fs'])) {
09407                                                                 break;
09408                                                         }
09409                                                         $filename = basename($pl['opt']['fs']);
09410                                                         if (isset($this->embeddedfiles[$filename]['n'])) {
09411                                                                 $annots .= ' /FS <</Type /Filespec /F '.$this->_datastring($filename, $annot_obj_id).' /EF <</F '.$this->embeddedfiles[$filename]['n'].' 0 R>> >>';
09412                                                                 $iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag');
09413                                                                 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
09414                                                                         $annots .= ' /Name /'.$pl['opt']['name'];
09415                                                                 } else {
09416                                                                         $annots .= ' /Name /PushPin';
09417                                                                 }
09418                                                         }
09419                                                         break;
09420                                                 }
09421                                                 case 'sound': {
09422                                                         if (!isset($pl['opt']['fs'])) {
09423                                                                 break;
09424                                                         }
09425                                                         $filename = basename($pl['opt']['fs']);
09426                                                         if (isset($this->embeddedfiles[$filename]['n'])) {
09427                                                                 // ... TO BE COMPLETED ...
09428                                                                 // /R /C /B /E /CO /CP
09429                                                                 $annots .= ' /Sound <</Type /Filespec /F '.$this->_datastring($filename, $annot_obj_id).' /EF <</F '.$this->embeddedfiles[$filename]['n'].' 0 R>> >>';
09430                                                                 $iconsapp = array('Speaker', 'Mic');
09431                                                                 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
09432                                                                         $annots .= ' /Name /'.$pl['opt']['name'];
09433                                                                 } else {
09434                                                                         $annots .= ' /Name /Speaker';
09435                                                                 }
09436                                                         }
09437                                                         break;
09438                                                 }
09439                                                 case 'movie': {
09440                                                         break;
09441                                                 }
09442                                                 case 'widget': {
09443                                                         $hmode = array('N', 'I', 'O', 'P', 'T');
09444                                                         if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmode)) {
09445                                                                 $annots .= ' /H /'.$pl['opt']['h'];
09446                                                         }
09447                                                         if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk'])) AND !empty($pl['opt']['mk'])) {
09448                                                                 $annots .= ' /MK <<';
09449                                                                 if (isset($pl['opt']['mk']['r'])) {
09450                                                                         $annots .= ' /R '.$pl['opt']['mk']['r'];
09451                                                                 }
09452                                                                 if (isset($pl['opt']['mk']['bc']) AND (is_array($pl['opt']['mk']['bc']))) {
09453                                                                         $annots .= ' /BC [';
09454                                                                         foreach($pl['opt']['mk']['bc'] AS $col) {
09455                                                                                 $col = intval($col);
09456                                                                                 $color = $col <= 0 ? 0 : ($col >= 255 ? 1 : $col / 255);
09457                                                                                 $annots .= sprintf(' %.2F', $color);
09458                                                                         }
09459                                                                         $annots .= ']';
09460                                                                 }
09461                                                                 if (isset($pl['opt']['mk']['bg']) AND (is_array($pl['opt']['mk']['bg']))) {
09462                                                                         $annots .= ' /BG [';
09463                                                                         foreach($pl['opt']['mk']['bg'] AS $col) {
09464                                                                                 $col = intval($col);
09465                                                                                 $color = $col <= 0 ? 0 : ($col >= 255 ? 1 : $col / 255);
09466                                                                                 $annots .= sprintf(' %.2F', $color);
09467                                                                         }
09468                                                                         $annots .= ']';
09469                                                                 }
09470                                                                 if (isset($pl['opt']['mk']['ca'])) {
09471                                                                         $annots .= ' /CA '.$pl['opt']['mk']['ca'];
09472                                                                 }
09473                                                                 if (isset($pl['opt']['mk']['rc'])) {
09474                                                                         $annots .= ' /RC '.$pl['opt']['mk']['rc'];
09475                                                                 }
09476                                                                 if (isset($pl['opt']['mk']['ac'])) {
09477                                                                         $annots .= ' /AC '.$pl['opt']['mk']['ac'];
09478                                                                 }
09479                                                                 if (isset($pl['opt']['mk']['i'])) {
09480                                                                         $info = $this->getImageBuffer($pl['opt']['mk']['i']);
09481                                                                         if ($info !== false) {
09482                                                                                 $annots .= ' /I '.$info['n'].' 0 R';
09483                                                                         }
09484                                                                 }
09485                                                                 if (isset($pl['opt']['mk']['ri'])) {
09486                                                                         $info = $this->getImageBuffer($pl['opt']['mk']['ri']);
09487                                                                         if ($info !== false) {
09488                                                                                 $annots .= ' /RI '.$info['n'].' 0 R';
09489                                                                         }
09490                                                                 }
09491                                                                 if (isset($pl['opt']['mk']['ix'])) {
09492                                                                         $info = $this->getImageBuffer($pl['opt']['mk']['ix']);
09493                                                                         if ($info !== false) {
09494                                                                                 $annots .= ' /IX '.$info['n'].' 0 R';
09495                                                                         }
09496                                                                 }
09497                                                                 if (isset($pl['opt']['mk']['if']) AND (is_array($pl['opt']['mk']['if'])) AND !empty($pl['opt']['mk']['if'])) {
09498                                                                         $annots .= ' /IF <<';
09499                                                                         $if_sw = array('A', 'B', 'S', 'N');
09500                                                                         if (isset($pl['opt']['mk']['if']['sw']) AND in_array($pl['opt']['mk']['if']['sw'], $if_sw)) {
09501                                                                                 $annots .= ' /SW /'.$pl['opt']['mk']['if']['sw'];
09502                                                                         }
09503                                                                         $if_s = array('A', 'P');
09504                                                                         if (isset($pl['opt']['mk']['if']['s']) AND in_array($pl['opt']['mk']['if']['s'], $if_s)) {
09505                                                                                 $annots .= ' /S /'.$pl['opt']['mk']['if']['s'];
09506                                                                         }
09507                                                                         if (isset($pl['opt']['mk']['if']['a']) AND (is_array($pl['opt']['mk']['if']['a'])) AND !empty($pl['opt']['mk']['if']['a'])) {
09508                                                                                 $annots .= sprintf(' /A [%.2F %.2F]', $pl['opt']['mk']['if']['a'][0], $pl['opt']['mk']['if']['a'][1]);
09509                                                                         }
09510                                                                         if (isset($pl['opt']['mk']['if']['fb']) AND ($pl['opt']['mk']['if']['fb'])) {
09511                                                                                 $annots .= ' /FB true';
09512                                                                         }
09513                                                                         $annots .= '>>';
09514                                                                 }
09515                                                                 if (isset($pl['opt']['mk']['tp']) AND ($pl['opt']['mk']['tp'] >= 0) AND ($pl['opt']['mk']['tp'] <= 6)) {
09516                                                                         $annots .= ' /TP '.intval($pl['opt']['mk']['tp']);
09517                                                                 }
09518                                                                 $annots .= '>>';
09519                                                         } // end MK
09520                                                         // --- Entries for field dictionaries ---
09521                                                         if (isset($this->radiobutton_groups[$n][$pl['txt']])) {
09522                                                                 // set parent
09523                                                                 $annots .= ' /Parent '.$this->radiobutton_groups[$n][$pl['txt']].' 0 R';
09524                                                         }
09525                                                         if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
09526                                                                 $annots .= ' /T '.$this->_datastring($pl['opt']['t'], $annot_obj_id);
09527                                                         }
09528                                                         if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
09529                                                                 $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $annot_obj_id);
09530                                                         }
09531                                                         if (isset($pl['opt']['tm']) AND is_string($pl['opt']['tm'])) {
09532                                                                 $annots .= ' /TM '.$this->_datastring($pl['opt']['tm'], $annot_obj_id);
09533                                                         }
09534                                                         if (isset($pl['opt']['ff'])) {
09535                                                                 if (is_array($pl['opt']['ff'])) {
09536                                                                         // array of bit settings
09537                                                                         $flag = 0;
09538                                                                         foreach($pl['opt']['ff'] as $val) {
09539                                                                                 $flag += 1 << ($val - 1);
09540                                                                         }
09541                                                                 } else {
09542                                                                         $flag = intval($pl['opt']['ff']);
09543                                                                 }
09544                                                                 $annots .= ' /Ff '.$flag;
09545                                                         }
09546                                                         if (isset($pl['opt']['maxlen'])) {
09547                                                                 $annots .= ' /MaxLen '.intval($pl['opt']['maxlen']);
09548                                                         }
09549                                                         if (isset($pl['opt']['v'])) {
09550                                                                 $annots .= ' /V';
09551                                                                 if (is_array($pl['opt']['v'])) {
09552                                                                         foreach ($pl['opt']['v'] AS $optval) {
09553                                                                                 if (is_float($optval)) {
09554                                                                                         $optval = sprintf('%.2F', $optval);
09555                                                                                 }
09556                                                                                 $annots .= ' '.$optval;
09557                                                                         }
09558                                                                 } else {
09559                                                                         $annots .= ' '.$this->_textstring($pl['opt']['v'], $annot_obj_id);
09560                                                                 }
09561                                                         }
09562                                                         if (isset($pl['opt']['dv'])) {
09563                                                                 $annots .= ' /DV';
09564                                                                 if (is_array($pl['opt']['dv'])) {
09565                                                                         foreach ($pl['opt']['dv'] AS $optval) {
09566                                                                                 if (is_float($optval)) {
09567                                                                                         $optval = sprintf('%.2F', $optval);
09568                                                                                 }
09569                                                                                 $annots .= ' '.$optval;
09570                                                                         }
09571                                                                 } else {
09572                                                                         $annots .= ' '.$this->_textstring($pl['opt']['dv'], $annot_obj_id);
09573                                                                 }
09574                                                         }
09575                                                         if (isset($pl['opt']['rv'])) {
09576                                                                 $annots .= ' /RV';
09577                                                                 if (is_array($pl['opt']['rv'])) {
09578                                                                         foreach ($pl['opt']['rv'] AS $optval) {
09579                                                                                 if (is_float($optval)) {
09580                                                                                         $optval = sprintf('%.2F', $optval);
09581                                                                                 }
09582                                                                                 $annots .= ' '.$optval;
09583                                                                         }
09584                                                                 } else {
09585                                                                         $annots .= ' '.$this->_textstring($pl['opt']['rv'], $annot_obj_id);
09586                                                                 }
09587                                                         }
09588                                                         if (isset($pl['opt']['a']) AND !empty($pl['opt']['a'])) {
09589                                                                 $annots .= ' /A << '.$pl['opt']['a'].' >>';
09590                                                         }
09591                                                         if (isset($pl['opt']['aa']) AND !empty($pl['opt']['aa'])) {
09592                                                                 $annots .= ' /AA << '.$pl['opt']['aa'].' >>';
09593                                                         }
09594                                                         if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
09595                                                                 $annots .= ' /DA ('.$pl['opt']['da'].')';
09596                                                         }
09597                                                         if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
09598                                                                 $annots .= ' /Q '.intval($pl['opt']['q']);
09599                                                         }
09600                                                         if (isset($pl['opt']['opt']) AND (is_array($pl['opt']['opt'])) AND !empty($pl['opt']['opt'])) {
09601                                                                 $annots .= ' /Opt [';
09602                                                                 foreach($pl['opt']['opt'] AS $copt) {
09603                                                                         if (is_array($copt)) {
09604                                                                                 $annots .= ' ['.$this->_textstring($copt[0], $annot_obj_id).' '.$this->_textstring($copt[1], $annot_obj_id).']';
09605                                                                         } else {
09606                                                                                 $annots .= ' '.$this->_textstring($copt, $annot_obj_id);
09607                                                                         }
09608                                                                 }
09609                                                                 $annots .= ']';
09610                                                         }
09611                                                         if (isset($pl['opt']['ti'])) {
09612                                                                 $annots .= ' /TI '.intval($pl['opt']['ti']);
09613                                                         }
09614                                                         if (isset($pl['opt']['i']) AND (is_array($pl['opt']['i'])) AND !empty($pl['opt']['i'])) {
09615                                                                 $annots .= ' /I [';
09616                                                                 foreach($pl['opt']['i'] AS $copt) {
09617                                                                         $annots .= intval($copt).' ';
09618                                                                 }
09619                                                                 $annots .= ']';
09620                                                         }
09621                                                         break;
09622                                                 }
09623                                                 case 'screen': {
09624                                                         break;
09625                                                 }
09626                                                 case 'printermark': {
09627                                                         break;
09628                                                 }
09629                                                 case 'trapnet': {
09630                                                         break;
09631                                                 }
09632                                                 case 'watermark': {
09633                                                         break;
09634                                                 }
09635                                                 case '3d': {
09636                                                         break;
09637                                                 }
09638                                                 default: {
09639                                                         break;
09640                                                 }
09641                                         }
09642                                         $annots .= '>>';
09643                                         // create new annotation object
09644                                         $this->_out($this->_getobj($annot_obj_id)."\n".$annots."\n".'endobj');
09645                                         if ($formfield AND !isset($this->radiobutton_groups[$n][$pl['txt']])) {
09646                                                 // store reference of form object
09647                                                 $this->form_obj_id[] = $annot_obj_id;
09648                                         }
09649                                 }
09650                         }
09651                 } // end for each page
09652         }
09653 
09663         protected function _putAPXObject($w=0, $h=0, $stream='') {
09664                 $stream = trim($stream);
09665                 $out = $this->_getobj()."\n";
09666                 $this->xobjects['AX'.$this->n] = array('n' => $this->n);
09667                 $out .= '<<';
09668                 $out .= ' /Type /XObject';
09669                 $out .= ' /Subtype /Form';
09670                 $out .= ' /FormType 1';
09671                 if ($this->compress) {
09672                         $stream = gzcompress($stream);
09673                         $out .= ' /Filter /FlateDecode';
09674                 }
09675                 $rect = sprintf('%.2F %.2F', $w, $h);
09676                 $out .= ' /BBox [0 0 '.$rect.']';
09677                 $out .= ' /Matrix [1 0 0 1 0 0]';
09678                 $out .= ' /Resources 2 0 R';
09679                 $stream = $this->_getrawstream($stream);
09680                 $out .= ' /Length '.strlen($stream);
09681                 $out .= ' >>';
09682                 $out .= ' stream'."\n".$stream."\n".'endstream';
09683                 $out .= "\n".'endobj';
09684                 $this->_out($out);
09685                 return $this->n;
09686         }
09687 
09697         protected function _getULONG($str, $offset) {
09698                 $v = unpack('Ni', substr($str, $offset, 4));
09699                 return $v['i'];
09700         }
09701 
09711         protected function _getUSHORT($str, $offset) {
09712                 $v = unpack('ni', substr($str, $offset, 2));
09713                 return $v['i'];
09714         }
09715 
09725         protected function _getSHORT($str, $offset) {
09726                 $v = unpack('si', substr($str, $offset, 2));
09727                 return $v['i'];
09728         }
09729 
09739         protected function _getFWORD($str, $offset) {
09740                 $v = $this->_getUSHORT($str, $offset);
09741                 if ($v > 0x7fff) {
09742                         $v -= 0x10000;
09743                 }
09744                 return $v;
09745         }
09746 
09756         protected function _getUFWORD($str, $offset) {
09757                 $v = $this->_getUSHORT($str, $offset);
09758                 return $v;
09759         }
09760 
09770         protected function _getFIXED($str, $offset) {
09771                 // mantissa
09772                 $m = $this->_getFWORD($str, $offset);
09773                 // fraction
09774                 $f = $this->_getUSHORT($str, ($offset + 2));
09775                 $v = floatval(''.$m.'.'.$f.'');
09776                 return $v;
09777         }
09778 
09788         protected function _getBYTE($str, $offset) {
09789                 $v = unpack('Ci', substr($str, $offset, 1));
09790                 return $v['i'];
09791         }
09802         protected function updateCIDtoGIDmap($map, $cid, $gid) {
09803                 if (($cid >= 0) AND ($cid <= 0xFFFF) AND ($gid >= 0)) {
09804                         if ($gid > 0xFFFF) {
09805                                 $gid -= 0x10000;
09806                         }
09807                         $map{($cid * 2)} = chr($gid >> 8);
09808                         $map{(($cid * 2) + 1)} = chr($gid & 0xFF);
09809                 }
09810                 return $map;
09811         }
09812 
09825         public function addTTFfont($fontfile, $fonttype='', $enc='', $flags=32, $outpath='') {
09826                 if (!file_exists($fontfile)) {
09827                         $this->Error('Could not find file: '.$fontfile.'');
09828                 }
09829                 // font metrics
09830                 $fmetric = array();
09831                 // build new font name for TCPDF compatibility
09832                 $font_path_parts = pathinfo($fontfile);
09833                 if (!isset($font_path_parts['filename'])) {
09834                         $font_path_parts['filename'] = substr($font_path_parts['basename'], 0, -(strlen($font_path_parts['extension']) + 1));
09835                 }
09836                 $font_name = strtolower($font_path_parts['filename']);
09837                 $font_name = preg_replace('/[^a-z0-9_]/', '', $font_name);
09838                 $search  = array('bold', 'oblique', 'italic', 'regular');
09839                 $replace = array('b', 'i', 'i', '');
09840                 $font_name = str_replace($search, $replace, $font_name);
09841                 if (empty($font_name)) {
09842                         // set generic name
09843                         $font_name = 'tcpdffont';
09844                 }
09845                 // set output path
09846                 if (empty($outpath)) {
09847                         $outpath = $this->_getfontpath();
09848                 }
09849                 // check if this font already exist
09850                 if (file_exists($outpath.$font_name.'.php')) {
09851                         // this font already exist (delete it from fonts folder to rebuild it)
09852                         return $font_name;
09853                 }
09854                 $fmetric['file'] = $font_name.'.z';
09855                 $fmetric['ctg'] = $font_name.'.ctg.z';
09856                 // get font data
09857                 $font = file_get_contents($fontfile);
09858                 $fmetric['originalsize'] = strlen($font);
09859                 // autodetect font type
09860                 if (empty($fonttype)) {
09861                         if ($this->_getULONG($font, 0) == 0x10000) {
09862                                 // True Type (Unicode or not)
09863                                 $fonttype = 'TrueTypeUnicode';
09864                         } elseif (substr($font, 0, 4) == 'OTTO') {
09865                                 // Open Type (Unicode or not)
09866                                 $this->Error('Unsupported font format: OpenType with CFF data.');
09867                         } else {
09868                                 // Type 1
09869                                 $fonttype = 'Type1';
09870                         }
09871                 }
09872                 // set font type
09873                 switch ($fonttype) {
09874                         case 'CID0CT':
09875                         case 'CID0CS':
09876                         case 'CID0KR':
09877                         case 'CID0JP': {
09878                                 $fmetric['type'] = 'cidfont0';
09879                                 break;
09880                         }
09881                         case 'Type1': {
09882                                 $fmetric['type'] = 'Type1';
09883                                 if (empty($enc) AND (($flags & 4) == 0)) {
09884                                         $enc = 'cp1252';
09885                                 }
09886                                 break;
09887                         }
09888                         case 'TrueType': {
09889                                 $fmetric['type'] = 'TrueType';
09890                                 break;
09891                         }
09892                         case 'TrueTypeUnicode':
09893                         default: {
09894                                 $fmetric['type'] = 'TrueTypeUnicode';
09895                                 break;
09896                         }
09897                 }
09898                 // set encoding maps (if any)
09899                 $fmetric['enc'] = preg_replace('/[^A-Za-z0-9_\-]/', '', $enc);
09900                 $fmetric['diff'] = '';
09901                 if (($fmetric['type'] == 'TrueType') OR ($fmetric['type'] == 'Type1')) {
09902                         if (!empty($enc) AND ($enc != 'cp1252') AND isset($this->encmaps->encmap[$enc])) {
09903                                 // build differences from reference encoding
09904                                 $enc_ref = $this->encmaps->encmap['cp1252'];
09905                                 $enc_target = $this->encmaps->encmap[$enc];
09906                                 $last = 0;
09907                                 for ($i = 32; $i <= 255; ++$i) {
09908                                         if ($enc_target != $enc_ref[$i]) {
09909                                                 if ($i != ($last + 1)) {
09910                                                         $fmetric['diff'] .= $i.' ';
09911                                                 }
09912                                                 $last = $i;
09913                                                 $fmetric['diff'] .= '/'.$enc_target[$i].' ';
09914                                         }
09915                                 }
09916                         }
09917                 }
09918                 // parse the font by type
09919                 if ($fmetric['type'] == 'Type1') {
09920                         // ---------- TYPE 1 ----------
09921                         // read first segment
09922                         $a = unpack('Cmarker/Ctype/Vsize', substr($font, 0, 6));
09923                         if ($a['marker'] != 128) {
09924                                 $this->Error('Font file is not a valid binary Type1');
09925                         }
09926                         $fmetric['size1'] = $a['size'];
09927                         $data = substr($font, 6, $fmetric['size1']);
09928                         // read second segment
09929                         $a = unpack('Cmarker/Ctype/Vsize', substr($font, (6 + $fmetric['size1']), 6));
09930                         if ($a['marker'] != 128) {
09931                                 $this->Error('Font file is not a valid binary Type1');
09932                         }
09933                         $fmetric['size2'] = $a['size'];
09934                         $encrypted = substr($font, (12 + $fmetric['size1']), $fmetric['size2']);
09935                         $data .= $encrypted;
09936                         // store compressed font
09937                         $fp = fopen($outpath.$fmetric['file'], 'wb');
09938                         fwrite($fp, gzcompress($data));
09939                         fclose($fp);
09940                         // get font info
09941                         $fmetric['Flags'] = $flags;
09942                         preg_match ('#/FullName[\s]*\(([^\)]*)#', $font, $matches);
09943                         $fmetric['name'] = preg_replace('/[^a-zA-Z0-9_\-]/', '', $matches[1]);
09944                         preg_match('#/FontBBox[\s]*{([^}]*)#', $font, $matches);
09945                         $fmetric['bbox'] = trim($matches[1]);
09946                         $bv = explode(' ', $fmetric['bbox']);
09947                         $fmetric['Ascent'] = intval($bv[3]);
09948                         $fmetric['Descent'] = intval($bv[1]);
09949                         preg_match('#/ItalicAngle[\s]*([0-9\+\-]*)#', $font, $matches);
09950                         $fmetric['italicAngle'] = intval($matches[1]);
09951                         if ($fmetric['italicAngle'] != 0) {
09952                                 $fmetric['Flags'] |= 64;
09953                         }
09954                         preg_match('#/UnderlinePosition[\s]*([0-9\+\-]*)#', $font, $matches);
09955                         $fmetric['underlinePosition'] = intval($matches[1]);
09956                         preg_match('#/UnderlineThickness[\s]*([0-9\+\-]*)#', $font, $matches);
09957                         $fmetric['underlineThickness'] = intval($matches[1]);
09958                         preg_match('#/isFixedPitch[\s]*([^\s]*)#', $font, $matches);
09959                         if ($matches[1] == 'true') {
09960                                 $fmetric['Flags'] |= 1;
09961                         }
09962                         // get internal map
09963                         $imap = array();
09964                         if (preg_match_all('#dup[\s]([0-9]+)[\s]*/([^\s]*)[\s]put#sU', $font, $fmap, PREG_SET_ORDER) > 0) {
09965                                 foreach ($fmap as $v) {
09966                                         $imap[$v[2]] = $v[1];
09967                                 }
09968                         }
09969                         // decrypt eexec encrypted part
09970                         $r = 55665; // eexec encryption constant
09971                         $c1 = 52845;
09972                         $c2 = 22719;
09973                         $elen = strlen($encrypted);
09974                         $eplain = '';
09975                         for ($i = 0; $i < $elen; ++$i) {
09976                                 $chr = ord($encrypted{$i});
09977                                 $eplain .= chr($chr ^ ($r >> 8));
09978                                 $r = ((($chr + $r) * $c1 + $c2) % 65536);
09979                         }
09980                         if (preg_match('#/ForceBold[\s]*([^\s]*)#', $eplain, $matches) > 0) {
09981                                 if ($matches[1] == 'true') {
09982                                         $fmetric['Flags'] |= 0x40000;
09983                                 }
09984                         }
09985                         if (preg_match('#/StdVW[\s]*\[([^\]]*)#', $eplain, $matches) > 0) {
09986                                 $fmetric['StemV'] = intval($matches[1]);
09987                         } else {
09988                                 $fmetric['StemV'] = 70;
09989                         }
09990                         if (preg_match('#/StdHW[\s]*\[([^\]]*)#', $eplain, $matches) > 0) {
09991                                 $fmetric['StemH'] = intval($matches[1]);
09992                         } else {
09993                                 $fmetric['StemH'] = 30;
09994                         }
09995                         if (preg_match('#/BlueValues[\s]*\[([^\]]*)#', $eplain, $matches) > 0) {
09996                                 $bv = explode(' ', $matches[1]);
09997                                 if (count($bv) >= 6) {
09998                                         $v1 = intval($bv[2]);
09999                                         $v2 = intval($bv[4]);
10000                                         if ($v1 <= $v2) {
10001                                                 $fmetric['XHeight'] = $v1;
10002                                                 $fmetric['CapHeight'] = $v2;
10003                                         } else {
10004                                                 $fmetric['XHeight'] = $v2;
10005                                                 $fmetric['CapHeight'] = $v1;
10006                                         }
10007                                 } else {
10008                                         $fmetric['XHeight'] = 450;
10009                                         $fmetric['CapHeight'] = 700;
10010                                 }
10011                         } else {
10012                                 $fmetric['XHeight'] = 450;
10013                                 $fmetric['CapHeight'] = 700;
10014                         }
10015                         // get the number of random bytes at the beginning of charstrings
10016                         if (preg_match('#/lenIV[\s]*([0-9]*)#', $eplain, $matches) > 0) {
10017                                 $lenIV = intval($matches[1]);
10018                         } else {
10019                                 $lenIV = 4;
10020                         }
10021                         $fmetric['Leading'] = 0;
10022                         // get charstring data
10023                         $eplain = substr($eplain, (strpos($eplain, '/CharStrings') + 1));
10024                         preg_match_all('#/([A-Za-z0-9\.]*)[\s][0-9]+[\s]RD[\s](.*)[\s]ND#sU', $eplain, $matches, PREG_SET_ORDER);
10025                         if (!empty($enc) AND isset($this->encmaps->encmap[$enc])) {
10026                                 $enc_map = $this->encmaps->encmap[$enc];
10027                         } else {
10028                                 $enc_map = false;
10029                         }
10030                         $fmetric['cw'] = '';
10031                         $fmetric['MaxWidth'] = 0;
10032                         $cwidths = array();
10033                         foreach ($matches as $k => $v) {
10034                                 $cid = 0;
10035                                 if (isset($imap[$v[1]])) {
10036                                         $cid = $imap[$v[1]];
10037                                 } elseif ($enc_map !== false) {
10038                                         $cid = array_search($v[1], $enc_map);
10039                                         if ($cid === false) {
10040                                                 $cid = 0;
10041                                         } elseif ($cid > 1000) {
10042                                                 $cid -= 1000;
10043                                         }
10044                                 }
10045                                 // decrypt charstring encrypted part
10046                                 $r = 4330; // charstring encryption constant
10047                                 $c1 = 52845;
10048                                 $c2 = 22719;
10049                                 $cd = $v[2];
10050                                 $clen = strlen($cd);
10051                                 $ccom = array();
10052                                 for ($i = 0; $i < $clen; ++$i) {
10053                                         $chr = ord($cd{$i});
10054                                         $ccom[] = ($chr ^ ($r >> 8));
10055                                         $r = ((($chr + $r) * $c1 + $c2) % 65536);
10056                                 }
10057                                 // decode numbers
10058                                 $cdec = array();
10059                                 $ck = 0;
10060                                 $i = $lenIV;
10061                                 while ($i < $clen) {
10062                                         if ($ccom[$i] < 32) {
10063                                                 $cdec[$ck] = $ccom[$i];
10064                                                 if (($ck > 0) AND ($cdec[$ck] == 13)) {
10065                                                         // hsbw command: update width
10066                                                         $cwidths[$cid] = $cdec[($ck - 1)];
10067                                                 }
10068                                                 ++$i;
10069                                         } elseif (($ccom[$i] >= 32) AND ($ccom[$i] <= 246)) {
10070                                                 $cdec[$ck] = ($ccom[$i] - 139);
10071                                                 ++$i;
10072                                         } elseif (($ccom[$i] >= 247) AND ($ccom[$i] <= 250)) {
10073                                                 $cdec[$ck] = ((($ccom[$i] - 247) * 256) + $ccom[($i + 1)] + 108);
10074                                                 $i += 2;
10075                                         } elseif (($ccom[$i] >= 251) AND ($ccom[$i] <= 254)) {
10076                                                 $cdec[$ck] = ((-($ccom[$i] - 251) * 256) - $ccom[($i + 1)] - 108);
10077                                                 $i += 2;
10078                                         } elseif ($ccom[$i] == 255) {
10079                                                 $sval = chr($ccom[($i + 1)]).chr($ccom[($i + 2)]).chr($ccom[($i + 3)]).chr($ccom[($i + 4)]);
10080                                                 $vsval = unpack('li', $sval);
10081                                                 $cdec[$ck] = $vsval['i'];
10082                                                 $i += 5;
10083                                         }
10084                                         ++$ck;
10085                                 }
10086                         } // end for each matches
10087                         $fmetric['MissingWidth'] = $cwidths[0];
10088                         $fmetric['MaxWidth'] = $fmetric['MissingWidth'];
10089                         $fmetric['AvgWidth'] = 0;
10090                         // set chars widths
10091                         for ($cid = 0; $cid <= 255; ++$cid) {
10092                                 if (isset($cwidths[$cid])) {
10093                                         if ($cwidths[$cid] > $fmetric['MaxWidth']) {
10094                                                 $fmetric['MaxWidth'] = $cwidths[$cid];
10095                                         }
10096                                         $fmetric['AvgWidth'] += $cwidths[$cid];
10097                                         $fmetric['cw'] .= ','.$cid.'=>'.$cwidths[$cid];
10098                                 } else {
10099                                         $fmetric['cw'] .= ','.$cid.'=>'.$fmetric['MissingWidth'];
10100                                 }
10101                         }
10102                         $fmetric['AvgWidth'] = round($fmetric['AvgWidth'] / count($cwidths));
10103                 } else {
10104                         // ---------- TRUE TYPE ----------
10105                         if ($fmetric['type'] != 'cidfont0') {
10106                                 // store compressed font
10107                                 $fp = fopen($outpath.$fmetric['file'], 'wb');
10108                                 fwrite($fp, gzcompress($font));
10109                                 fclose($fp);
10110                         }
10111                         $offset = 0; // offset position of the font data
10112                         if ($this->_getULONG($font, $offset) != 0x10000) {
10113                                 // sfnt version must be 0x00010000 for TrueType version 1.0.
10114                                 return $font;
10115                         }
10116                         $offset += 4;
10117                         // get number of tables
10118                         $numTables = $this->_getUSHORT($font, $offset);
10119                         $offset += 2;
10120                         // skip searchRange, entrySelector and rangeShift
10121                         $offset += 6;
10122                         // tables array
10123                         $table = array();
10124                         // ---------- get tables ----------
10125                         for ($i = 0; $i < $numTables; ++$i) {
10126                                 // get table info
10127                                 $tag = substr($font, $offset, 4);
10128                                 $offset += 4;
10129                                 $table[$tag] = array();
10130                                 $table[$tag]['checkSum'] = $this->_getULONG($font, $offset);
10131                                 $offset += 4;
10132                                 $table[$tag]['offset'] = $this->_getULONG($font, $offset);
10133                                 $offset += 4;
10134                                 $table[$tag]['length'] = $this->_getULONG($font, $offset);
10135                                 $offset += 4;
10136                         }
10137                         // check magicNumber
10138                         $offset = $table['head']['offset'] + 12;
10139                         if ($this->_getULONG($font, $offset) != 0x5F0F3CF5) {
10140                                 // magicNumber must be 0x5F0F3CF5
10141                                 return $font;
10142                         }
10143                         $offset += 4;
10144                         $offset += 2; // skip flags
10145                         // get FUnits
10146                         $fmetric['unitsPerEm'] = $this->_getUSHORT($font, $offset);
10147                         $offset += 2;
10148                         // units ratio constant
10149                         $urk = (1000 / $fmetric['unitsPerEm']);
10150                         $offset += 16; // skip created, modified
10151                         $xMin = round($this->_getFWORD($font, $offset) * $urk);
10152                         $offset += 2;
10153                         $yMin = round($this->_getFWORD($font, $offset) * $urk);
10154                         $offset += 2;
10155                         $xMax = round($this->_getFWORD($font, $offset) * $urk);
10156                         $offset += 2;
10157                         $yMax = round($this->_getFWORD($font, $offset) * $urk);
10158                         $offset += 2;
10159                         $fmetric['bbox'] = ''.$xMin.' '.$yMin.' '.$xMax.' '.$yMax.'';
10160                         $macStyle = $this->_getUSHORT($font, $offset);
10161                         $offset += 2;
10162                         // PDF font flags
10163                         $fmetric['Flags'] = $flags;
10164                         if (($macStyle & 2) == 2) {
10165                                 // italic flag
10166                                 $fmetric['Flags'] |= 64;
10167                         }
10168                         // get offset mode (indexToLocFormat : 0 = short, 1 = long)
10169                         $offset = $table['head']['offset'] + 50;
10170                         $short_offset = ($this->_getSHORT($font, $offset) == 0);
10171                         $offset += 2;
10172                         // get the offsets to the locations of the glyphs in the font, relative to the beginning of the glyphData table
10173                         $indexToLoc = array();
10174                         $offset = $table['loca']['offset'];
10175                         if ($short_offset) {
10176                                 // short version
10177                                 $tot_num_glyphs = ($table['loca']['length'] / 2); // numGlyphs + 1
10178                                 for ($i = 0; $i < $tot_num_glyphs; ++$i) {
10179                                         $indexToLoc[$i] = $this->_getUSHORT($font, $offset) * 2;
10180                                         $offset += 2;
10181                                 }
10182                         } else {
10183                                 // long version
10184                                 $tot_num_glyphs = ($table['loca']['length'] / 4); // numGlyphs + 1
10185                                 for ($i = 0; $i < $tot_num_glyphs; ++$i) {
10186                                         $indexToLoc[$i] = $this->_getULONG($font, $offset);
10187                                         $offset += 4;
10188                                 }
10189                         }
10190                         // get glyphs indexes of chars from cmap table
10191                         $offset = $table['cmap']['offset'] + 2;
10192                         $numEncodingTables = $this->_getUSHORT($font, $offset);
10193                         $offset += 2;
10194                         $encodingTables = array();
10195                         for ($i = 0; $i < $numEncodingTables; ++$i) {
10196                                 $encodingTables[$i]['platformID'] = $this->_getUSHORT($font, $offset);
10197                                 $offset += 2;
10198                                 $encodingTables[$i]['encodingID'] = $this->_getUSHORT($font, $offset);
10199                                 $offset += 2;
10200                                 $encodingTables[$i]['offset'] = $this->_getULONG($font, $offset);
10201                                 $offset += 4;
10202                         }
10203                         // ---------- get os/2 metrics ----------
10204                         $offset = $table['OS/2']['offset'];
10205                         $offset += 2; // skip version
10206                         // xAvgCharWidth
10207                         $fmetric['AvgWidth'] = round($this->_getFWORD($font, $offset) * $urk);
10208                         $offset += 2;
10209                         // usWeightClass
10210                         $usWeightClass = round($this->_getUFWORD($font, $offset) * $urk);
10211                         // estimate StemV and StemH (400 = usWeightClass for Normal - Regular font)
10212                         $fmetric['StemV'] = round((70 * $usWeightClass) / 400);
10213                         $fmetric['StemH'] = round((30 * $usWeightClass) / 400);
10214                         $offset += 2;
10215                         $offset += 2; // usWidthClass
10216                         $fsType = $this->_getSHORT($font, $offset);
10217                         $offset += 2;
10218                         if ($fsType == 2) {
10219                                 $this->Error('This Font cannot be modified, embedded or exchanged in any manner without first obtaining permission of the legal owner.');
10220                         }
10221                         // ---------- get font name ----------
10222                         $fmetric['name'] = '';
10223                         $offset = $table['name']['offset'];
10224                         $offset += 2; // skip Format selector (=0).
10225                         // Number of NameRecords that follow n.
10226                         $numNameRecords = $this->_getUSHORT($font, $offset);
10227                         $offset += 2;
10228                         // Offset to start of string storage (from start of table).
10229                         $stringStorageOffset = $this->_getUSHORT($font, $offset);
10230                         $offset += 2;
10231                         for ($i = 0; $i < $numNameRecords; ++$i) {
10232                                 $offset += 6; // skip Platform ID, Platform-specific encoding ID, Language ID.
10233                                 // Name ID.
10234                                 $nameID = $this->_getUSHORT($font, $offset);
10235                                 $offset += 2;
10236                                 if ($nameID == 6) {
10237                                         // String length (in bytes).
10238                                         $stringLength = $this->_getUSHORT($font, $offset);
10239                                         $offset += 2;
10240                                         // String offset from start of storage area (in bytes).
10241                                         $stringOffset = $this->_getUSHORT($font, $offset);
10242                                         $offset += 2;
10243                                         $offset = ($table['name']['offset'] + $stringStorageOffset + $stringOffset);
10244                                         $fmetric['name'] = substr($font, $offset, $stringLength);
10245                                         $fmetric['name'] = preg_replace('/[^a-zA-Z0-9_\-]/', '', $fmetric['name']);
10246                                         break;
10247                                 } else {
10248                                         $offset += 4; // skip String length, String offset
10249                                 }
10250                         }
10251                         if (empty($fmetric['name'])) {
10252                                 $fmetric['name'] = $font_name;
10253                         }
10254                         // ---------- get post data ----------
10255                         $offset = $table['post']['offset'];
10256                         $offset += 4; // skip Format Type
10257                         $fmetric['italicAngle'] = $this->_getFIXED($font, $offset);
10258                         $offset += 4;
10259                         $fmetric['underlinePosition'] = round($this->_getFWORD($font, $offset) * $urk);
10260                         $offset += 2;
10261                         $fmetric['underlineThickness'] = round($this->_getFWORD($font, $offset) * $urk);
10262                         $offset += 2;
10263                         $isFixedPitch = ($this->_getULONG($font, $offset) == 0) ? false : true;
10264                         $offset += 2;
10265                         if ($isFixedPitch) {
10266                                 $fmetric['Flags'] |= 1;
10267                         }
10268                         // ---------- get hhea data ----------
10269                         $offset = $table['hhea']['offset'];
10270                         $offset += 4; // skip Table version number
10271                         // Ascender
10272                         $fmetric['Ascent'] = round($this->_getFWORD($font, $offset) * $urk);
10273                         $offset += 2;
10274                         // Descender
10275                         $fmetric['Descent'] = round($this->_getFWORD($font, $offset) * $urk);
10276                         $offset += 2;
10277                         // LineGap
10278                         $fmetric['Leading'] = round($this->_getFWORD($font, $offset) * $urk);
10279                         $offset += 2;
10280                         // advanceWidthMax
10281                         $fmetric['MaxWidth'] = round($this->_getUFWORD($font, $offset) * $urk);
10282                         $offset += 2;
10283                         $offset += 22; // skip some values
10284                         // get the number of hMetric entries in hmtx table
10285                         $numberOfHMetrics = $this->_getUSHORT($font, $offset);
10286                         // ---------- get maxp data ----------
10287                         $offset = $table['maxp']['offset'];
10288                         $offset += 4; // skip Table version number
10289                         // get the the number of glyphs in the font.
10290                         $numGlyphs = $this->_getUSHORT($font, $offset);
10291                         // ---------- get CIDToGIDMap ----------
10292                         $ctg = array();
10293                         foreach ($encodingTables as $enctable) {
10294                                 if (($enctable['platformID'] == 3) AND ($enctable['encodingID'] == 0)) {
10295                                         $modesymbol = true;
10296                                 } else {
10297                                         $modesymbol = false;
10298                                 }
10299                                 $offset = $table['cmap']['offset'] + $enctable['offset'];
10300                                 $format = $this->_getUSHORT($font, $offset);
10301                                 $offset += 2;
10302                                 switch ($format) {
10303                                         case 0: { // Format 0: Byte encoding table
10304                                                 $offset += 4; // skip length and version/language
10305                                                 for ($c = 0; $c < 256; ++$c) {
10306                                                         $g = $this->_getBYTE($font, $offset);
10307                                                         $ctg[$c] = $g;
10308                                                         ++$offset;
10309                                                 }
10310                                                 break;
10311                                         }
10312                                         case 2: { // Format 2: High-byte mapping through table
10313                                                 $offset += 4; // skip length and version/language
10314                                                 $numSubHeaders = 0;
10315                                                 for ($i = 0; $i < 256; ++$i) {
10316                                                         // Array that maps high bytes to subHeaders: value is subHeader index * 8.
10317                                                         $subHeaderKeys[$i] = ($this->_getUSHORT($font, $offset) / 8);
10318                                                         $offset += 2;
10319                                                         if ($numSubHeaders < $subHeaderKeys[$i]) {
10320                                                                 $numSubHeaders = $subHeaderKeys[$i];
10321                                                         }
10322                                                 }
10323                                                 // the number of subHeaders is equal to the max of subHeaderKeys + 1
10324                                                 ++$numSubHeaders;
10325                                                 // read subHeader structures
10326                                                 $subHeaders = array();
10327                                                 $numGlyphIndexArray = 0;
10328                                                 for ($k = 0; $k < $numSubHeaders; ++$k) {
10329                                                         $subHeaders[$k]['firstCode'] = $this->_getUSHORT($font, $offset);
10330                                                         $offset += 2;
10331                                                         $subHeaders[$k]['entryCount'] = $this->_getUSHORT($font, $offset);
10332                                                         $offset += 2;
10333                                                         $subHeaders[$k]['idDelta'] = $this->_getSHORT($font, $offset);
10334                                                         $offset += 2;
10335                                                         $subHeaders[$k]['idRangeOffset'] = $this->_getUSHORT($font, $offset);
10336                                                         $offset += 2;
10337                                                         $subHeaders[$k]['idRangeOffset'] -= (2 + (($numSubHeaders - $k - 1) * 8));
10338                                                         $subHeaders[$k]['idRangeOffset'] /= 2;
10339                                                         $numGlyphIndexArray += $subHeaders[$k]['entryCount'];
10340                                                 }
10341                                                 for ($k = 0; $k < $numGlyphIndexArray; ++$k) {
10342                                                         $glyphIndexArray[$k] = $this->_getUSHORT($font, $offset);
10343                                                         $offset += 2;
10344                                                 }
10345                                                 for ($i = 0; $i < 256; ++$i) {
10346                                                         $k = $subHeaderKeys[$i];
10347                                                         if ($k == 0) {
10348                                                                 // one byte code
10349                                                                 $c = $i;
10350                                                                 $g = $glyphIndexArray[0];
10351                                                                 $ctg[$c] = $g;
10352                                                         } else {
10353                                                                 // two bytes code
10354                                                                 $start_byte = $subHeaders[$k]['firstCode'];
10355                                                                 $end_byte = $start_byte + $subHeaders[$k]['entryCount'];
10356                                                                 for ($j = $start_byte; $j < $end_byte; ++$j) {
10357                                                                         // combine high and low bytes
10358                                                                         $c = (($i << 8) + $j);
10359                                                                         $idRangeOffset = ($subHeaders[$k]['idRangeOffset'] + $j - $subHeaders[$k]['firstCode']);
10360                                                                         $g = $glyphIndexArray[$idRangeOffset];
10361                                                                         $g += ($idDelta[$k] - 65536);
10362                                                                         if ($g < 0) {
10363                                                                                 $g = 0;
10364                                                                         }
10365                                                                         $ctg[$c] = $g;
10366                                                                 }
10367                                                         }
10368                                                 }
10369                                                 break;
10370                                         }
10371                                         case 4: { // Format 4: Segment mapping to delta values
10372                                                 $length = $this->_getUSHORT($font, $offset);
10373                                                 $offset += 2;
10374                                                 $offset += 2; // skip version/language
10375                                                 $segCount = ($this->_getUSHORT($font, $offset) / 2);
10376                                                 $offset += 2;
10377                                                 $offset += 6; // skip searchRange, entrySelector, rangeShift
10378                                                 $endCount = array(); // array of end character codes for each segment
10379                                                 for ($k = 0; $k < $segCount; ++$k) {
10380                                                         $endCount[$k] = $this->_getUSHORT($font, $offset);
10381                                                         $offset += 2;
10382                                                 }
10383                                                 $offset += 2; // skip reservedPad
10384                                                 $startCount = array(); // array of start character codes for each segment
10385                                                 for ($k = 0; $k < $segCount; ++$k) {
10386                                                         $startCount[$k] = $this->_getUSHORT($font, $offset);
10387                                                         $offset += 2;
10388                                                 }
10389                                                 $idDelta = array(); // delta for all character codes in segment
10390                                                 for ($k = 0; $k < $segCount; ++$k) {
10391                                                         $idDelta[$k] = $this->_getUSHORT($font, $offset);
10392                                                         $offset += 2;
10393                                                 }
10394                                                 $idRangeOffset = array(); // Offsets into glyphIdArray or 0
10395                                                 for ($k = 0; $k < $segCount; ++$k) {
10396                                                         $idRangeOffset[$k] = $this->_getUSHORT($font, $offset);
10397                                                         $offset += 2;
10398                                                 }
10399                                                 $gidlen = ($length / 2) - 8 - (4 * $segCount);
10400                                                 $glyphIdArray = array(); // glyph index array
10401                                                 for ($k = 0; $k < $gidlen; ++$k) {
10402                                                         $glyphIdArray[$k] = $this->_getUSHORT($font, $offset);
10403                                                         $offset += 2;
10404                                                 }
10405                                                 for ($k = 0; $k < $segCount; ++$k) {
10406                                                         for ($c = $startCount[$k]; $c <= $endCount[$k]; ++$c) {
10407                                                                 if ($idRangeOffset[$k] == 0) {
10408                                                                         $g = $c;
10409                                                                 } else {
10410                                                                         $gid = (($idRangeOffset[$k] / 2) + ($c - $startCount[$k]) - ($segCount - $k));
10411                                                                         $g = $glyphIdArray[$gid];
10412                                                                 }
10413                                                                 $g += ($idDelta[$k] - 65536);
10414                                                                 if ($g < 0) {
10415                                                                         $g = 0;
10416                                                                 }
10417                                                                 $ctg[$c] = $g;
10418                                                         }
10419                                                 }
10420                                                 break;
10421                                         }
10422                                         case 6: { // Format 6: Trimmed table mapping
10423                                                 $offset += 4; // skip length and version/language
10424                                                 $firstCode = $this->_getUSHORT($font, $offset);
10425                                                 $offset += 2;
10426                                                 $entryCount = $this->_getUSHORT($font, $offset);
10427                                                 $offset += 2;
10428                                                 for ($k = 0; $k < $entryCount; ++$k) {
10429                                                         $c = ($k + $firstCode);
10430                                                         $g = $this->_getUSHORT($font, $offset);
10431                                                         $ctg[$c] = $g;
10432                                                         $offset += 2;
10433                                                 }
10434                                                 break;
10435                                         }
10436                                         case 8: { // Format 8: Mixed 16-bit and 32-bit coverage
10437                                                 $offset += 10; // skip reserved, length and version/language
10438                                                 for ($k = 0; $k < 8192; ++$k) {
10439                                                         $is32[$k] = $this->_getBYTE($font, $offset);
10440                                                         ++$offset;
10441                                                 }
10442                                                 $nGroups = $this->_getULONG($font, $offset);
10443                                                 $offset += 4;
10444                                                 for ($i = 0; $i < $nGroups; ++$i) {
10445                                                         $startCharCode = $this->_getULONG($font, $offset);
10446                                                         $offset += 4;
10447                                                         $endCharCode = $this->_getULONG($font, $offset);
10448                                                         $offset += 4;
10449                                                         $startGlyphID = $this->_getULONG($font, $offset);
10450                                                         $offset += 4;
10451                                                         for ($k = $startCharCode; $k <= $endCharCode; ++$k) {
10452                                                                 $is32idx = floor($c / 8);
10453                                                                 if ((isset($is32[$is32idx])) AND (($is32[$is32idx] & (1 << (7 - ($c % 8)))) == 0)) {
10454                                                                         $c = $k;
10455                                                                 } else {
10456                                                                         // 32 bit format
10457                                                                         // convert to decimal (http://www.unicode.org/faq//utf_bom.html#utf16-4)
10458                                                                         //LEAD_OFFSET = (0xD800 - (0x10000 >> 10)) = 55232
10459                                                                         //SURROGATE_OFFSET = (0x10000 - (0xD800 << 10) - 0xDC00) = -56613888
10460                                                                         $c = ((55232 + ($k >> 10)) << 10) + (0xDC00 + ($k & 0x3FF)) -56613888;
10461                                                                 }
10462                                                                 $ctg[$c] = $g;
10463                                                                 ++$startGlyphID;
10464                                                         }
10465                                                 }
10466                                                 break;
10467                                         }
10468                                         case 10: { // Format 10: Trimmed array
10469                                                 $offset += 10; // skip reserved, length and version/language
10470                                                 $startCharCode = $this->_getULONG($font, $offset);
10471                                                 $offset += 4;
10472                                                 $numChars = $this->_getULONG($font, $offset);
10473                                                 $offset += 4;
10474                                                 for ($k = 0; $k < $numChars; ++$k) {
10475                                                         $c = ($k + $startCharCode);
10476                                                         $g = $this->_getUSHORT($font, $offset);
10477                                                         $ctg[$c] = $g;
10478                                                         $offset += 2;
10479                                                 }
10480                                                 break;
10481                                         }
10482                                         case 12: { // Format 12: Segmented coverage
10483                                                 $offset += 10; // skip length and version/language
10484                                                 $nGroups = $this->_getULONG($font, $offset);
10485                                                 $offset += 4;
10486                                                 for ($k = 0; $k < $nGroups; ++$k) {
10487                                                         $startCharCode = $this->_getULONG($font, $offset);
10488                                                         $offset += 4;
10489                                                         $endCharCode = $this->_getULONG($font, $offset);
10490                                                         $offset += 4;
10491                                                         $startGlyphCode = $this->_getULONG($font, $offset);
10492                                                         $offset += 4;
10493                                                         for ($c = $startCharCode; $c <= $endCharCode; ++$c) {
10494                                                                 $ctg[$c] = $startGlyphCode;
10495                                                                 ++$startGlyphCode;
10496                                                         }
10497                                                 }
10498                                                 break;
10499                                         }
10500                                         case 13: { // Format 13: Many-to-one range mappings
10501                                                 // to be implemented ...
10502                                                 break;
10503                                         }
10504                                         case 14: { // Format 14: Unicode Variation Sequences
10505                                                 // to be implemented ...
10506                                                 break;
10507                                         }
10508                                 }
10509                         }
10510                         if (!isset($ctg[0])) {
10511                                 $ctg[0] = 0;
10512                         }
10513                         // get xHeight (height of x)
10514                         $offset = ($table['glyf']['offset'] + $indexToLoc[$ctg[120]] + 4);
10515                         $yMin = $this->_getFWORD($font, $offset);
10516                         $offset += 4;
10517                         $yMax = $this->_getFWORD($font, $offset);
10518                         $offset += 2;
10519                         $fmetric['XHeight'] = round(($yMax - $yMin) * $urk);
10520                         // get CapHeight (height of H)
10521                         $offset = ($table['glyf']['offset'] + $indexToLoc[$ctg[72]] + 4);
10522                         $yMin = $this->_getFWORD($font, $offset);
10523                         $offset += 4;
10524                         $yMax = $this->_getFWORD($font, $offset);
10525                         $offset += 2;
10526                         $fmetric['CapHeight'] = round(($yMax - $yMin) * $urk);
10527                         // ceate widths array
10528                         $cw = array();
10529                         $offset = $table['hmtx']['offset'];
10530                         for ($i = 0 ; $i < $numberOfHMetrics; ++$i) {
10531                                 $cw[$i] = round($this->_getUFWORD($font, $offset) * $urk);
10532                                 $offset += 4; // skip lsb
10533                         }
10534                         if ($numberOfHMetrics < $numGlyphs) {
10535                                 // fill missing widths with the last value
10536                                 $cw = array_pad($cw, $numGlyphs, $cw[($numberOfHMetrics - 1)]);
10537                         }
10538                         $fmetric['MissingWidth'] = $cw[0];
10539                         $fmetric['cw'] = '';
10540                         for ($cid = 0; $cid <= 65535; ++$cid) {
10541                                 if (isset($ctg[$cid]) AND isset($cw[$ctg[$cid]])) {
10542                                         $fmetric['cw'] .= ','.$cid.'=>'.$cw[$ctg[$cid]];
10543                                 }
10544                         }
10545                 } // end of true type
10546                 if (($fmetric['type'] == 'TrueTypeUnicode') AND (count($ctg) == 256)) {
10547                         $fmetric['type'] == 'TrueType';
10548                 }
10549                 // ---------- create php font file ----------
10550                 $pfile = '<'.'?'.'php'."\n";
10551                 $pfile .= '// TCPDF FONT FILE DESCRIPTION'."\n";
10552                 $pfile .= '$type=\''.$fmetric['type'].'\';'."\n";
10553                 $pfile .= '$name=\''.$fmetric['name'].'\';'."\n";
10554                 $pfile .= '$up='.$fmetric['underlinePosition'].';'."\n";
10555                 $pfile .= '$ut='.$fmetric['underlineThickness'].';'."\n";
10556                 if ($fmetric['MissingWidth'] > 0) {
10557                         $pfile .= '$dw='.$fmetric['MissingWidth'].';'."\n";
10558                 } else {
10559                         $pfile .= '$dw='.$fmetric['AvgWidth'].';'."\n";
10560                 }
10561                 $pfile .= '$diff=\''.$fmetric['diff'].'\';'."\n";
10562                 if ($fmetric['type'] == 'Type1') {
10563                         // Type 1
10564                         $pfile .= '$enc=\''.$fmetric['enc'].'\';'."\n";
10565                         $pfile .= '$file=\''.$fmetric['file'].'\';'."\n";
10566                         $pfile .= '$size1='.$fmetric['size1'].';'."\n";
10567                         $pfile .= '$size2='.$fmetric['size2'].';'."\n";
10568                 } else {
10569                         $pfile .= '$originalsize='.$fmetric['originalsize'].';'."\n";
10570                         if ($fmetric['type'] == 'cidfont0') {
10571                                 // CID-0
10572                                 switch ($fonttype) {
10573                                         case 'CID0JP': {
10574                                                 $pfile .= '// Japanese'."\n";
10575                                                 $pfile .= '$enc=\'UniJIS-UTF16-H\';'."\n";
10576                                                 $pfile .= '$cidinfo=array(\'Registry\'=>\'Adobe\', \'Ordering\'=>\'Japan1\',\'Supplement\'=>5);'."\n";
10577                                                 $pfile .= 'include(dirname(__FILE__).\'/uni2cid_aj16.php\');'."\n";
10578                                                 break;
10579                                         }
10580                                         case 'CID0KR': {
10581                                                 $pfile .= '// Korean'."\n";
10582                                                 $pfile .= '$enc=\'UniKS-UTF16-H\';'."\n";
10583                                                 $pfile .= '$cidinfo=array(\'Registry\'=>\'Adobe\', \'Ordering\'=>\'Korea1\',\'Supplement\'=>0);'."\n";
10584                                                 $pfile .= 'include(dirname(__FILE__).\'/uni2cid_ak12.php\');'."\n";
10585                                                 break;
10586                                         }
10587                                         case 'CID0CS': {
10588                                                 $pfile .= '// Chinese Simplified'."\n";
10589                                                 $pfile .= '$enc=\'UniGB-UTF16-H\';'."\n";
10590                                                 $pfile .= '$cidinfo=array(\'Registry\'=>\'Adobe\', \'Ordering\'=>\'GB1\',\'Supplement\'=>2);'."\n";
10591                                                 $pfile .= 'include(dirname(__FILE__).\'/uni2cid_ag15.php\');'."\n";
10592                                                 break;
10593                                         }
10594                                         case 'CID0CT':
10595                                         default: {
10596                                                 $pfile .= '// Chinese Traditional'."\n";
10597                                                 $pfile .= '$enc=\'UniCNS-UTF16-H\';'."\n";
10598                                                 $pfile .= '$cidinfo=array(\'Registry\'=>\'Adobe\', \'Ordering\'=>\'CNS1\',\'Supplement\'=>0);'."\n";
10599                                                 $pfile .= 'include(dirname(__FILE__).\'/uni2cid_aj16.php\');'."\n";
10600                                                 break;
10601                                         }
10602                                 }
10603                         } else {
10604                                 // TrueType
10605                                 $pfile .= '$enc=\''.$fmetric['enc'].'\';'."\n";
10606                                 $pfile .= '$file=\''.$fmetric['file'].'\';'."\n";
10607                                 $pfile .= '$ctg=\''.$fmetric['ctg'].'\';'."\n";
10608                                 // create CIDToGIDMap
10609                                 $cidtogidmap = str_pad('', 131072, "\x00"); // (256 * 256 * 2) = 131072
10610                                 foreach ($ctg as $cid => $gid) {
10611                                         $cidtogidmap = $this->updateCIDtoGIDmap($cidtogidmap, $cid, $ctg[$cid]);
10612                                 }
10613                                 // store compressed CIDToGIDMap
10614                                 $fp = fopen($outpath.$fmetric['ctg'], 'wb');
10615                                 fwrite($fp, gzcompress($cidtogidmap));
10616                                 fclose($fp);
10617                         }
10618                 }
10619                 $pfile .= '$desc=array(';
10620                 $pfile .= '\'Flags\'=>'.$fmetric['Flags'].',';
10621                 $pfile .= '\'FontBBox\'=>\'['.$fmetric['bbox'].']\',';
10622                 $pfile .= '\'ItalicAngle\'=>'.$fmetric['italicAngle'].',';
10623                 $pfile .= '\'Ascent\'=>'.$fmetric['Ascent'].',';
10624                 $pfile .= '\'Descent\'=>'.$fmetric['Descent'].',';
10625                 $pfile .= '\'Leading\'=>'.$fmetric['Leading'].',';
10626                 $pfile .= '\'CapHeight\'=>'.$fmetric['CapHeight'].',';
10627                 $pfile .= '\'XHeight\'=>'.$fmetric['XHeight'].',';
10628                 $pfile .= '\'StemV\'=>'.$fmetric['StemV'].',';
10629                 $pfile .= '\'StemH\'=>'.$fmetric['StemH'].',';
10630                 $pfile .= '\'AvgWidth\'=>'.$fmetric['AvgWidth'].',';
10631                 $pfile .= '\'MaxWidth\'=>'.$fmetric['MaxWidth'].',';
10632                 $pfile .= '\'MissingWidth\'=>'.$fmetric['MissingWidth'].'';
10633                 $pfile .= ');'."\n";
10634                 $pfile .= '$cw=array('.substr($fmetric['cw'], 1).');'."\n";
10635                 $pfile .= '// --- EOF ---'."\n";
10636                 // store file
10637                 $fp = fopen($outpath.$font_name.'.php', 'w');
10638                 fwrite($fp, $pfile);
10639                 fclose($fp);
10640                 // return TCPDF font name
10641                 return $font_name;
10642         }
10643 
10653         protected function _getTrueTypeFontSubset($font, $subsetchars) {
10654                 ksort($subsetchars);
10655                 $offset = 0; // offset position of the font data
10656                 if ($this->_getULONG($font, $offset) != 0x10000) {
10657                         // sfnt version must be 0x00010000 for TrueType version 1.0.
10658                         return $font;
10659                 }
10660                 $offset += 4;
10661                 // get number of tables
10662                 $numTables = $this->_getUSHORT($font, $offset);
10663                 $offset += 2;
10664                 // skip searchRange, entrySelector and rangeShift
10665                 $offset += 6;
10666                 // tables array
10667                 $table = array();
10668                 // for each table
10669                 for ($i = 0; $i < $numTables; ++$i) {
10670                         // get table info
10671                         $tag = substr($font, $offset, 4);
10672                         $offset += 4;
10673                         $table[$tag] = array();
10674                         $table[$tag]['checkSum'] = $this->_getULONG($font, $offset);
10675                         $offset += 4;
10676                         $table[$tag]['offset'] = $this->_getULONG($font, $offset);
10677                         $offset += 4;
10678                         $table[$tag]['length'] = $this->_getULONG($font, $offset);
10679                         $offset += 4;
10680                 }
10681                 // check magicNumber
10682                 $offset = $table['head']['offset'] + 12;
10683                 if ($this->_getULONG($font, $offset) != 0x5F0F3CF5) {
10684                         // magicNumber must be 0x5F0F3CF5
10685                         return $font;
10686                 }
10687                 $offset += 4;
10688                 // get offset mode (indexToLocFormat : 0 = short, 1 = long)
10689                 $offset = $table['head']['offset'] + 50;
10690                 $short_offset = ($this->_getSHORT($font, $offset) == 0);
10691                 $offset += 2;
10692                 // get the offsets to the locations of the glyphs in the font, relative to the beginning of the glyphData table
10693                 $indexToLoc = array();
10694                 $offset = $table['loca']['offset'];
10695                 if ($short_offset) {
10696                         // short version
10697                         $tot_num_glyphs = ($table['loca']['length'] / 2); // numGlyphs + 1
10698                         for ($i = 0; $i < $tot_num_glyphs; ++$i) {
10699                                 $indexToLoc[$i] = $this->_getUSHORT($font, $offset) * 2;
10700                                 $offset += 2;
10701                         }
10702                 } else {
10703                         // long version
10704                         $tot_num_glyphs = ($table['loca']['length'] / 4); // numGlyphs + 1
10705                         for ($i = 0; $i < $tot_num_glyphs; ++$i) {
10706                                 $indexToLoc[$i] = $this->_getULONG($font, $offset);
10707                                 $offset += 4;
10708                         }
10709                 }
10710                 // get glyphs indexes of chars from cmap table
10711                 $subsetglyphs = array(); // glyph IDs on key
10712                 $subsetglyphs[0] = true; // character codes that do not correspond to any glyph in the font should be mapped to glyph index 0
10713                 $offset = $table['cmap']['offset'] + 2;
10714                 $numEncodingTables = $this->_getUSHORT($font, $offset);
10715                 $offset += 2;
10716                 $encodingTables = array();
10717                 for ($i = 0; $i < $numEncodingTables; ++$i) {
10718                         $encodingTables[$i]['platformID'] = $this->_getUSHORT($font, $offset);
10719                         $offset += 2;
10720                         $encodingTables[$i]['encodingID'] = $this->_getUSHORT($font, $offset);
10721                         $offset += 2;
10722                         $encodingTables[$i]['offset'] = $this->_getULONG($font, $offset);
10723                         $offset += 4;
10724                 }
10725                 foreach ($encodingTables as $enctable) {
10726                         if (($enctable['platformID'] == 3) AND ($enctable['encodingID'] == 0)) {
10727                                 $modesymbol = true;
10728                         } else {
10729                                 $modesymbol = false;
10730                         }
10731                         $offset = $table['cmap']['offset'] + $enctable['offset'];
10732                         $format = $this->_getUSHORT($font, $offset);
10733                         $offset += 2;
10734                         switch ($format) {
10735                                 case 0: { // Format 0: Byte encoding table
10736                                         $offset += 4; // skip length and version/language
10737                                         for ($c = 0; $c < 256; ++$c) {
10738                                                 if (isset($subsetchars[$c])) {
10739                                                         $g = $this->_getBYTE($font, $offset);
10740                                                         $subsetglyphs[$g] = true;
10741                                                 }
10742                                                 ++$offset;
10743                                         }
10744                                         break;
10745                                 }
10746                                 case 2: { // Format 2: High-byte mapping through table
10747                                         $offset += 4; // skip length and version/language
10748                                         $numSubHeaders = 0;
10749                                         for ($i = 0; $i < 256; ++$i) {
10750                                                 // Array that maps high bytes to subHeaders: value is subHeader index * 8.
10751                                                 $subHeaderKeys[$i] = ($this->_getUSHORT($font, $offset) / 8);
10752                                                 $offset += 2;
10753                                                 if ($numSubHeaders < $subHeaderKeys[$i]) {
10754                                                         $numSubHeaders = $subHeaderKeys[$i];
10755                                                 }
10756                                         }
10757                                         // the number of subHeaders is equal to the max of subHeaderKeys + 1
10758                                         ++$numSubHeaders;
10759                                         // read subHeader structures
10760                                         $subHeaders = array();
10761                                         $numGlyphIndexArray = 0;
10762                                         for ($k = 0; $k < $numSubHeaders; ++$k) {
10763                                                 $subHeaders[$k]['firstCode'] = $this->_getUSHORT($font, $offset);
10764                                                 $offset += 2;
10765                                                 $subHeaders[$k]['entryCount'] = $this->_getUSHORT($font, $offset);
10766                                                 $offset += 2;
10767                                                 $subHeaders[$k]['idDelta'] = $this->_getSHORT($font, $offset);
10768                                                 $offset += 2;
10769                                                 $subHeaders[$k]['idRangeOffset'] = $this->_getUSHORT($font, $offset);
10770                                                 $offset += 2;
10771                                                 $subHeaders[$k]['idRangeOffset'] -= (2 + (($numSubHeaders - $k - 1) * 8));
10772                                                 $subHeaders[$k]['idRangeOffset'] /= 2;
10773                                                 $numGlyphIndexArray += $subHeaders[$k]['entryCount'];
10774                                         }
10775                                         for ($k = 0; $k < $numGlyphIndexArray; ++$k) {
10776                                                 $glyphIndexArray[$k] = $this->_getUSHORT($font, $offset);
10777                                                 $offset += 2;
10778                                         }
10779                                         for ($i = 0; $i < 256; ++$i) {
10780                                                 $k = $subHeaderKeys[$i];
10781                                                 if ($k == 0) {
10782                                                         // one byte code
10783                                                         $c = $i;
10784                                                         if (isset($subsetchars[$c])) {
10785                                                                 $g = $glyphIndexArray[0];
10786                                                                 $subsetglyphs[$g] = true;
10787                                                         }
10788                                                 } else {
10789                                                         // two bytes code
10790                                                         $start_byte = $subHeaders[$k]['firstCode'];
10791                                                         $end_byte = $start_byte + $subHeaders[$k]['entryCount'];
10792                                                         for ($j = $start_byte; $j < $end_byte; ++$j) {
10793                                                                 // combine high and low bytes
10794                                                                 $c = (($i << 8) + $j);
10795                                                                 if (isset($subsetchars[$c])) {
10796                                                                         $idRangeOffset = ($subHeaders[$k]['idRangeOffset'] + $j - $subHeaders[$k]['firstCode']);
10797                                                                         $g = $glyphIndexArray[$idRangeOffset];
10798                                                                         $g += ($idDelta[$k] - 65536);
10799                                                                         if ($g < 0) {
10800                                                                                 $g = 0;
10801                                                                         }
10802                                                                         $subsetglyphs[$g] = true;
10803                                                                 }
10804                                                         }
10805                                                 }
10806                                         }
10807                                         break;
10808                                 }
10809                                 case 4: { // Format 4: Segment mapping to delta values
10810                                         $length = $this->_getUSHORT($font, $offset);
10811                                         $offset += 2;
10812                                         $offset += 2; // skip version/language
10813                                         $segCount = ($this->_getUSHORT($font, $offset) / 2);
10814                                         $offset += 2;
10815                                         $offset += 6; // skip searchRange, entrySelector, rangeShift
10816                                         $endCount = array(); // array of end character codes for each segment
10817                                         for ($k = 0; $k < $segCount; ++$k) {
10818                                                 $endCount[$k] = $this->_getUSHORT($font, $offset);
10819                                                 $offset += 2;
10820                                         }
10821                                         $offset += 2; // skip reservedPad
10822                                         $startCount = array(); // array of start character codes for each segment
10823                                         for ($k = 0; $k < $segCount; ++$k) {
10824                                                 $startCount[$k] = $this->_getUSHORT($font, $offset);
10825                                                 $offset += 2;
10826                                         }
10827                                         $idDelta = array(); // delta for all character codes in segment
10828                                         for ($k = 0; $k < $segCount; ++$k) {
10829                                                 $idDelta[$k] = $this->_getUSHORT($font, $offset);
10830                                                 $offset += 2;
10831                                         }
10832                                         $idRangeOffset = array(); // Offsets into glyphIdArray or 0
10833                                         for ($k = 0; $k < $segCount; ++$k) {
10834                                                 $idRangeOffset[$k] = $this->_getUSHORT($font, $offset);
10835                                                 $offset += 2;
10836                                         }
10837                                         $gidlen = ($length / 2) - 8 - (4 * $segCount);
10838                                         $glyphIdArray = array(); // glyph index array
10839                                         for ($k = 0; $k < $gidlen; ++$k) {
10840                                                 $glyphIdArray[$k] = $this->_getUSHORT($font, $offset);
10841                                                 $offset += 2;
10842                                         }
10843                                         for ($k = 0; $k < $segCount; ++$k) {
10844                                                 for ($c = $startCount[$k]; $c <= $endCount[$k]; ++$c) {
10845                                                         if (isset($subsetchars[$c])) {
10846                                                                 if ($idRangeOffset[$k] == 0) {
10847                                                                         $g = $c;
10848                                                                 } else {
10849                                                                         $gid = (($idRangeOffset[$k] / 2) + ($c - $startCount[$k]) - ($segCount - $k));
10850                                                                         $g = $glyphIdArray[$gid];
10851                                                                 }
10852                                                                 $g += ($idDelta[$k] - 65536);
10853                                                                 if ($g < 0) {
10854                                                                         $g = 0;
10855                                                                 }
10856                                                                 $subsetglyphs[$g] = true;
10857                                                         }
10858                                                 }
10859                                         }
10860                                         break;
10861                                 }
10862                                 case 6: { // Format 6: Trimmed table mapping
10863                                         $offset += 4; // skip length and version/language
10864                                         $firstCode = $this->_getUSHORT($font, $offset);
10865                                         $offset += 2;
10866                                         $entryCount = $this->_getUSHORT($font, $offset);
10867                                         $offset += 2;
10868                                         for ($k = 0; $k < $entryCount; ++$k) {
10869                                                 $c = ($k + $firstCode);
10870                                                 if (isset($subsetchars[$c])) {
10871                                                         $g = $this->_getUSHORT($font, $offset);
10872                                                         $subsetglyphs[$g] = true;
10873                                                 }
10874                                                 $offset += 2;
10875                                         }
10876                                         break;
10877                                 }
10878                                 case 8: { // Format 8: Mixed 16-bit and 32-bit coverage
10879                                         $offset += 10; // skip reserved, length and version/language
10880                                         for ($k = 0; $k < 8192; ++$k) {
10881                                                 $is32[$k] = $this->_getBYTE($font, $offset);
10882                                                 ++$offset;
10883                                         }
10884                                         $nGroups = $this->_getULONG($font, $offset);
10885                                         $offset += 4;
10886                                         for ($i = 0; $i < $nGroups; ++$i) {
10887                                                 $startCharCode = $this->_getULONG($font, $offset);
10888                                                 $offset += 4;
10889                                                 $endCharCode = $this->_getULONG($font, $offset);
10890                                                 $offset += 4;
10891                                                 $startGlyphID = $this->_getULONG($font, $offset);
10892                                                 $offset += 4;
10893                                                 for ($k = $startCharCode; $k <= $endCharCode; ++$k) {
10894                                                         $is32idx = floor($c / 8);
10895                                                         if ((isset($is32[$is32idx])) AND (($is32[$is32idx] & (1 << (7 - ($c % 8)))) == 0)) {
10896                                                                 $c = $k;
10897                                                         } else {
10898                                                                 // 32 bit format
10899                                                                 // convert to decimal (http://www.unicode.org/faq//utf_bom.html#utf16-4)
10900                                                                 //LEAD_OFFSET = (0xD800 - (0x10000 >> 10)) = 55232
10901                                                                 //SURROGATE_OFFSET = (0x10000 - (0xD800 << 10) - 0xDC00) = -56613888
10902                                                                 $c = ((55232 + ($k >> 10)) << 10) + (0xDC00 + ($k & 0x3FF)) -56613888;
10903                                                         }
10904                                                         if (isset($subsetchars[$c])) {
10905                                                                 $subsetglyphs[$startGlyphID] = true;
10906                                                         }
10907                                                         ++$startGlyphID;
10908                                                 }
10909                                         }
10910                                         break;
10911                                 }
10912                                 case 10: { // Format 10: Trimmed array
10913                                         $offset += 10; // skip reserved, length and version/language
10914                                         $startCharCode = $this->_getULONG($font, $offset);
10915                                         $offset += 4;
10916                                         $numChars = $this->_getULONG($font, $offset);
10917                                         $offset += 4;
10918                                         for ($k = 0; $k < $numChars; ++$k) {
10919                                                 $c = ($k + $startCharCode);
10920                                                 if (isset($subsetchars[$c])) {
10921                                                         $g = $this->_getUSHORT($font, $offset);
10922                                                         $subsetglyphs[$g] = true;
10923                                                 }
10924                                                 $offset += 2;
10925                                         }
10926                                         break;
10927                                 }
10928                                 case 12: { // Format 12: Segmented coverage
10929                                         $offset += 10; // skip length and version/language
10930                                         $nGroups = $this->_getULONG($font, $offset);
10931                                         $offset += 4;
10932                                         for ($k = 0; $k < $nGroups; ++$k) {
10933                                                 $startCharCode = $this->_getULONG($font, $offset);
10934                                                 $offset += 4;
10935                                                 $endCharCode = $this->_getULONG($font, $offset);
10936                                                 $offset += 4;
10937                                                 $startGlyphCode = $this->_getULONG($font, $offset);
10938                                                 $offset += 4;
10939                                                 for ($c = $startCharCode; $c <= $endCharCode; ++$c) {
10940                                                         if (isset($subsetchars[$c])) {
10941                                                                 $subsetglyphs[$startGlyphCode] = true;
10942                                                         }
10943                                                         ++$startGlyphCode;
10944                                                 }
10945                                         }
10946                                         break;
10947                                 }
10948                                 case 13: { // Format 13: Many-to-one range mappings
10949                                         // to be implemented ...
10950                                         break;
10951                                 }
10952                                 case 14: { // Format 14: Unicode Variation Sequences
10953                                         // to be implemented ...
10954                                         break;
10955                                 }
10956                         }
10957                 }
10958                 // include all parts of composite glyphs
10959                 $new_sga = $subsetglyphs;
10960                 while (!empty($new_sga)) {
10961                         $sga = $new_sga;
10962                         $new_sga = array();
10963                         foreach ($sga as $key => $val) {
10964                                 if (isset($indexToLoc[$key])) {
10965                                         $offset = ($table['glyf']['offset'] + $indexToLoc[$key]);
10966                                         $numberOfContours = $this->_getSHORT($font, $offset);
10967                                         $offset += 2;
10968                                         if ($numberOfContours < 0) { // composite glyph
10969                                                 $offset += 8; // skip xMin, yMin, xMax, yMax
10970                                                 do {
10971                                                         $flags = $this->_getUSHORT($font, $offset);
10972                                                         $offset += 2;
10973                                                         $glyphIndex = $this->_getUSHORT($font, $offset);
10974                                                         $offset += 2;
10975                                                         if (!isset($subsetglyphs[$glyphIndex])) {
10976                                                                 // add missing glyphs
10977                                                                 $new_sga[$glyphIndex] = true;
10978                                                         }
10979                                                         // skip some bytes by case
10980                                                         if ($flags & 1) {
10981                                                                 $offset += 4;
10982                                                         } else {
10983                                                                 $offset += 2;
10984                                                         }
10985                                                         if ($flags & 8) {
10986                                                                 $offset += 2;
10987                                                         } elseif ($flags & 64) {
10988                                                                 $offset += 4;
10989                                                         } elseif ($flags & 128) {
10990                                                                 $offset += 8;
10991                                                         }
10992                                                 } while ($flags & 32);
10993                                         }
10994                                 }
10995                         }
10996                         $subsetglyphs += $new_sga;
10997                 }
10998                 // sort glyphs by key (and remove duplicates)
10999                 ksort($subsetglyphs);
11000                 // build new glyf and loca tables
11001                 $glyf = '';
11002                 $loca = '';
11003                 $offset = 0;
11004                 $glyf_offset = $table['glyf']['offset'];
11005                 for ($i = 0; $i < $tot_num_glyphs; ++$i) {
11006                         if (isset($subsetglyphs[$i])) {
11007                                 $length = ($indexToLoc[($i + 1)] - $indexToLoc[$i]);
11008                                 $glyf .= substr($font, ($glyf_offset + $indexToLoc[$i]), $length);
11009                         } else {
11010                                 $length = 0;
11011                         }
11012                         if ($short_offset) {
11013                                 $loca .= pack('n', ($offset / 2));
11014                         } else {
11015                                 $loca .= pack('N', $offset);
11016                         }
11017                         $offset += $length;
11018                 }
11019                 // array of table names to preserve (loca and glyf tables will be added later)
11020                 // the cmap table is not needed and shall not be present, since the mapping from character codes to glyph descriptions is provided separately
11021                 $table_names = array ('head', 'hhea', 'hmtx', 'maxp', 'cvt ', 'fpgm', 'prep'); // minimum required table names
11022                 // get the tables to preserve
11023                 $offset = 12;
11024                 foreach ($table as $tag => $val) {
11025                         if (in_array($tag, $table_names)) {
11026                                 $table[$tag]['data'] = substr($font, $table[$tag]['offset'], $table[$tag]['length']);
11027                                 if ($tag == 'head') {
11028                                         // set the checkSumAdjustment to 0
11029                                         $table[$tag]['data'] = substr($table[$tag]['data'], 0, 8)."\x0\x0\x0\x0".substr($table[$tag]['data'], 12);
11030                                 }
11031                                 $pad = 4 - ($table[$tag]['length'] % 4);
11032                                 if ($pad != 4) {
11033                                         // the length of a table must be a multiple of four bytes
11034                                         $table[$tag]['length'] += $pad;
11035                                         $table[$tag]['data'] .= str_repeat("\x0", $pad);
11036                                 }
11037                                 $table[$tag]['offset'] = $offset;
11038                                 $offset += $table[$tag]['length'];
11039                                 // check sum is not changed (so keep the following line commented)
11040                                 //$table[$tag]['checkSum'] = $this->_getTTFtableChecksum($table[$tag]['data'], $table[$tag]['length']);
11041                         } else {
11042                                 unset($table[$tag]);
11043                         }
11044                 }
11045                 // add loca
11046                 $table['loca']['data'] = $loca;
11047                 $table['loca']['length'] = strlen($loca);
11048                 $pad = 4 - ($table['loca']['length'] % 4);
11049                 if ($pad != 4) {
11050                         // the length of a table must be a multiple of four bytes
11051                         $table['loca']['length'] += $pad;
11052                         $table['loca']['data'] .= str_repeat("\x0", $pad);
11053                 }
11054                 $table['loca']['offset'] = $offset;
11055                 $table['loca']['checkSum'] = $this->_getTTFtableChecksum($table['loca']['data'], $table['loca']['length']);
11056                 $offset += $table['loca']['length'];
11057                 // add glyf
11058                 $table['glyf']['data'] = $glyf;
11059                 $table['glyf']['length'] = strlen($glyf);
11060                 $pad = 4 - ($table['glyf']['length'] % 4);
11061                 if ($pad != 4) {
11062                         // the length of a table must be a multiple of four bytes
11063                         $table['glyf']['length'] += $pad;
11064                         $table['glyf']['data'] .= str_repeat("\x0", $pad);
11065                 }
11066                 $table['glyf']['offset'] = $offset;
11067                 $table['glyf']['checkSum'] = $this->_getTTFtableChecksum($table['glyf']['data'], $table['glyf']['length']);
11068                 // rebuild font
11069                 $font = '';
11070                 $font .= pack('N', 0x10000); // sfnt version
11071                 $numTables = count($table);
11072                 $font .= pack('n', $numTables); // numTables
11073                 $entrySelector = floor(log($numTables, 2));
11074                 $searchRange = pow(2, $entrySelector) * 16;
11075                 $rangeShift = ($numTables * 16) - $searchRange;
11076                 $font .= pack('n', $searchRange); // searchRange
11077                 $font .= pack('n', $entrySelector); // entrySelector
11078                 $font .= pack('n', $rangeShift); // rangeShift
11079                 $offset = ($numTables * 16);
11080                 foreach ($table as $tag => $data) {
11081                         $font .= $tag; // tag
11082                         $font .= pack('N', $data['checkSum']); // checkSum
11083                         $font .= pack('N', ($data['offset'] + $offset)); // offset
11084                         $font .= pack('N', $data['length']); // length
11085                 }
11086                 foreach ($table as $data) {
11087                         $font .= $data['data'];
11088                 }
11089                 // set checkSumAdjustment on head table
11090                 $checkSumAdjustment = 0xB1B0AFBA - $this->_getTTFtableChecksum($font, strlen($font));
11091                 $font = substr($font, 0, $table['head']['offset'] + 8).pack('N', $checkSumAdjustment).substr($font, $table['head']['offset'] + 12);
11092                 return $font;
11093         }
11094 
11104         protected function _getTTFtableChecksum($table, $length) {
11105                 $sum = 0;
11106                 $tlen = ($length / 4);
11107                 $offset = 0;
11108                 for ($i = 0; $i < $tlen; ++$i) {
11109                         $v = unpack('Ni', substr($table, $offset, 4));
11110                         $sum += $v['i'];
11111                         $offset += 4;
11112                 }
11113                 $sum = unpack('Ni', pack('N', $sum));
11114                 return $sum['i'];
11115         }
11116 
11126         protected function _putfontwidths($font, $cidoffset=0) {
11127                 ksort($font['cw']);
11128                 $rangeid = 0;
11129                 $range = array();
11130                 $prevcid = -2;
11131                 $prevwidth = -1;
11132                 $interval = false;
11133                 // for each character
11134                 foreach ($font['cw'] as $cid => $width) {
11135                         $cid -= $cidoffset;
11136                         if ($font['subset'] AND ($cid > 255) AND (!isset($font['subsetchars'][$cid]))) {
11137                                 // ignore the unused characters (font subsetting)
11138                                 continue;
11139                         }
11140                         if ($width != $font['dw']) {
11141                                 if ($cid == ($prevcid + 1)) {
11142                                         // consecutive CID
11143                                         if ($width == $prevwidth) {
11144                                                 if ($width == $range[$rangeid][0]) {
11145                                                         $range[$rangeid][] = $width;
11146                                                 } else {
11147                                                         array_pop($range[$rangeid]);
11148                                                         // new range
11149                                                         $rangeid = $prevcid;
11150                                                         $range[$rangeid] = array();
11151                                                         $range[$rangeid][] = $prevwidth;
11152                                                         $range[$rangeid][] = $width;
11153                                                 }
11154                                                 $interval = true;
11155                                                 $range[$rangeid]['interval'] = true;
11156                                         } else {
11157                                                 if ($interval) {
11158                                                         // new range
11159                                                         $rangeid = $cid;
11160                                                         $range[$rangeid] = array();
11161                                                         $range[$rangeid][] = $width;
11162                                                 } else {
11163                                                         $range[$rangeid][] = $width;
11164                                                 }
11165                                                 $interval = false;
11166                                         }
11167                                 } else {
11168                                         // new range
11169                                         $rangeid = $cid;
11170                                         $range[$rangeid] = array();
11171                                         $range[$rangeid][] = $width;
11172                                         $interval = false;
11173                                 }
11174                                 $prevcid = $cid;
11175                                 $prevwidth = $width;
11176                         }
11177                 }
11178                 // optimize ranges
11179                 $prevk = -1;
11180                 $nextk = -1;
11181                 $prevint = false;
11182                 foreach ($range as $k => $ws) {
11183                         $cws = count($ws);
11184                         if (($k == $nextk) AND (!$prevint) AND ((!isset($ws['interval'])) OR ($cws < 4))) {
11185                                 if (isset($range[$k]['interval'])) {
11186                                         unset($range[$k]['interval']);
11187                                 }
11188                                 $range[$prevk] = array_merge($range[$prevk], $range[$k]);
11189                                 unset($range[$k]);
11190                         } else {
11191                                 $prevk = $k;
11192                         }
11193                         $nextk = $k + $cws;
11194                         if (isset($ws['interval'])) {
11195                                 if ($cws > 3) {
11196                                         $prevint = true;
11197                                 } else {
11198                                         $prevint = false;
11199                                 }
11200                                 unset($range[$k]['interval']);
11201                                 --$nextk;
11202                         } else {
11203                                 $prevint = false;
11204                         }
11205                 }
11206                 // output data
11207                 $w = '';
11208                 foreach ($range as $k => $ws) {
11209                         if (count(array_count_values($ws)) == 1) {
11210                                 // interval mode is more compact
11211                                 $w .= ' '.$k.' '.($k + count($ws) - 1).' '.$ws[0];
11212                         } else {
11213                                 // range mode
11214                                 $w .= ' '.$k.' [ '.implode(' ', $ws).' ]';
11215                         }
11216                 }
11217                 return '/W ['.$w.' ]';
11218         }
11219 
11225         protected function _putfonts() {
11226                 $nf = $this->n;
11227                 foreach ($this->diffs as $diff) {
11228                         //Encodings
11229                         $this->_newobj();
11230                         $this->_out('<< /Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.'] >>'."\n".'endobj');
11231                 }
11232                 $mqr = $this->get_mqr();
11233                 $this->set_mqr(false);
11234                 foreach ($this->FontFiles as $file => $info) {
11235                         // search and get font file to embedd
11236                         $fontdir = $info['fontdir'];
11237                         $file = strtolower($file);
11238                         $fontfile = '';
11239                         // search files on various directories
11240                         if (($fontdir !== false) AND file_exists($fontdir.$file)) {
11241                                 $fontfile = $fontdir.$file;
11242                         } elseif (file_exists($this->_getfontpath().$file)) {
11243                                 $fontfile = $this->_getfontpath().$file;
11244                         } elseif (file_exists($file)) {
11245                                 $fontfile = $file;
11246                         }
11247                         if (!$this->empty_string($fontfile)) {
11248                                 $font = file_get_contents($fontfile);
11249                                 $compressed = (substr($file, -2) == '.z');
11250                                 if ((!$compressed) AND (isset($info['length2']))) {
11251                                         $header = (ord($font{0}) == 128);
11252                                         if ($header) {
11253                                                 // strip first binary header
11254                                                 $font = substr($font, 6);
11255                                         }
11256                                         if ($header AND (ord($font{$info['length1']}) == 128)) {
11257                                                 // strip second binary header
11258                                                 $font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] + 6));
11259                                         }
11260                                 } elseif ($info['subset'] AND ((!$compressed) OR ($compressed AND function_exists('gzcompress')))) {
11261                                         if ($compressed) {
11262                                                 // uncompress font
11263                                                 $font = gzuncompress($font);
11264                                         }
11265                                         // merge subset characters
11266                                         $subsetchars = array(); // used chars
11267                                         foreach ($info['fontkeys'] as $fontkey) {
11268                                                 $fontinfo = $this->getFontBuffer($fontkey);
11269                                                 $subsetchars += $fontinfo['subsetchars'];
11270                                         }
11271                                         // rebuild a font subset
11272                                         $font = $this->_getTrueTypeFontSubset($font, $subsetchars);
11273                                         // calculate new font length
11274                                         $info['length1'] = strlen($font);
11275                                         if ($compressed) {
11276                                                 // recompress font
11277                                                 $font = gzcompress($font);
11278                                         }
11279                                 }
11280                                 $this->_newobj();
11281                                 $this->FontFiles[$file]['n'] = $this->n;
11282                                 $stream = $this->_getrawstream($font);
11283                                 $out = '<< /Length '.strlen($stream);
11284                                 if ($compressed) {
11285                                         $out .= ' /Filter /FlateDecode';
11286                                 }
11287                                 $out .= ' /Length1 '.$info['length1'];
11288                                 if (isset($info['length2'])) {
11289                                         $out .= ' /Length2 '.$info['length2'].' /Length3 0';
11290                                 }
11291                                 $out .= ' >>';
11292                                 $out .= ' stream'."\n".$stream."\n".'endstream';
11293                                 $out .= "\n".'endobj';
11294                                 $this->_out($out);
11295                         }
11296                 }
11297                 $this->set_mqr($mqr);
11298                 foreach ($this->fontkeys as $k) {
11299                         //Font objects
11300                         $font = $this->getFontBuffer($k);
11301                         $type = $font['type'];
11302                         $name = $font['name'];
11303                         if ($type == 'core') {
11304                                 // standard core font
11305                                 $out = $this->_getobj($this->font_obj_ids[$k])."\n";
11306                                 $out .= '<</Type /Font';
11307                                 $out .= ' /Subtype /Type1';
11308                                 $out .= ' /BaseFont /'.$name;
11309                                 $out .= ' /Name /F'.$font['i'];
11310                                 if ((strtolower($name) != 'symbol') AND (strtolower($name) != 'zapfdingbats')) {
11311                                         $out .= ' /Encoding /WinAnsiEncoding';
11312                                 }
11313                                 if ($k == 'helvetica') {
11314                                         // add default font for annotations
11315                                         $this->annotation_fonts[$k] = $font['i'];
11316                                 }
11317                                 $out .= ' >>';
11318                                 $out .= "\n".'endobj';
11319                                 $this->_out($out);
11320                         } elseif (($type == 'Type1') OR ($type == 'TrueType')) {
11321                                 // additional Type1 or TrueType font
11322                                 $out = $this->_getobj($this->font_obj_ids[$k])."\n";
11323                                 $out .= '<</Type /Font';
11324                                 $out .= ' /Subtype /'.$type;
11325                                 $out .= ' /BaseFont /'.$name;
11326                                 $out .= ' /Name /F'.$font['i'];
11327                                 $out .= ' /FirstChar 32 /LastChar 255';
11328                                 $out .= ' /Widths '.($this->n + 1).' 0 R';
11329                                 $out .= ' /FontDescriptor '.($this->n + 2).' 0 R';
11330                                 if ($font['enc']) {
11331                                         if (isset($font['diff'])) {
11332                                                 $out .= ' /Encoding '.($nf + $font['diff']).' 0 R';
11333                                         } else {
11334                                                 $out .= ' /Encoding /WinAnsiEncoding';
11335                                         }
11336                                 }
11337                                 $out .= ' >>';
11338                                 $out .= "\n".'endobj';
11339                                 $this->_out($out);
11340                                 // Widths
11341                                 $this->_newobj();
11342                                 $s = '[';
11343                                 for ($i = 32; $i < 256; ++$i) {
11344                                         $s .= $font['cw'][$i].' ';
11345                                 }
11346                                 $s .= ']';
11347                                 $s .= "\n".'endobj';
11348                                 $this->_out($s);
11349                                 //Descriptor
11350                                 $this->_newobj();
11351                                 $s = '<</Type /FontDescriptor /FontName /'.$name;
11352                                 foreach ($font['desc'] as $fdk => $fdv) {
11353                                         if (is_float($fdv)) {
11354                                                 $fdv = sprintf('%.3F', $fdv);
11355                                         }
11356                                         $s .= ' /'.$fdk.' '.$fdv.'';
11357                                 }
11358                                 if (!$this->empty_string($font['file'])) {
11359                                         $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R';
11360                                 }
11361                                 $s .= '>>';
11362                                 $s .= "\n".'endobj';
11363                                 $this->_out($s);
11364                         } else {
11365                                 // additional types
11366                                 $mtd = '_put'.strtolower($type);
11367                                 if (!method_exists($this, $mtd)) {
11368                                         $this->Error('Unsupported font type: '.$type);
11369                                 }
11370                                 $this->$mtd($font);
11371                         }
11372                 }
11373         }
11374 
11383         protected function _puttruetypeunicode($font) {
11384                 $fontname = '';
11385                 if ($font['subset']) {
11386                         // change name for font subsetting
11387                         $subtag = sprintf('%06u', $font['i']);
11388                         $subtag = strtr($subtag, '0123456789', 'ABCDEFGHIJ');
11389                         $fontname .= $subtag.'+';
11390                 }
11391                 $fontname .= $font['name'];
11392                 // Type0 Font
11393                 // A composite font composed of other fonts, organized hierarchically
11394                 $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n";
11395                 $out .= '<< /Type /Font';
11396                 $out .= ' /Subtype /Type0';
11397                 $out .= ' /BaseFont /'.$fontname;
11398                 $out .= ' /Name /F'.$font['i'];
11399                 $out .= ' /Encoding /'.$font['enc'];
11400                 $out .= ' /ToUnicode '.($this->n + 1).' 0 R';
11401                 $out .= ' /DescendantFonts ['.($this->n + 2).' 0 R]';
11402                 $out .= ' >>';
11403                 $out .= "\n".'endobj';
11404                 $this->_out($out);
11405                 // ToUnicode map for Identity-H
11406                 $stream = "/CIDInit /ProcSet findresource begin\n";
11407                 $stream .= "12 dict begin\n";
11408                 $stream .= "begincmap\n";
11409                 $stream .= "/CIDSystemInfo << /Registry (Adobe) /Ordering (UCS) /Supplement 0 >> def\n";
11410                 $stream .= "/CMapName /Adobe-Identity-UCS def\n";
11411                 $stream .= "/CMapType 2 def\n";
11412                 $stream .= "/WMode 0 def\n";
11413                 $stream .= "1 begincodespacerange\n";
11414                 $stream .= "<0000> <FFFF>\n";
11415                 $stream .= "endcodespacerange\n";
11416                 $stream .= "100 beginbfrange\n";
11417                 $stream .= "<0000> <00ff> <0000>\n";
11418                 $stream .= "<0100> <01ff> <0100>\n";
11419                 $stream .= "<0200> <02ff> <0200>\n";
11420                 $stream .= "<0300> <03ff> <0300>\n";
11421                 $stream .= "<0400> <04ff> <0400>\n";
11422                 $stream .= "<0500> <05ff> <0500>\n";
11423                 $stream .= "<0600> <06ff> <0600>\n";
11424                 $stream .= "<0700> <07ff> <0700>\n";
11425                 $stream .= "<0800> <08ff> <0800>\n";
11426                 $stream .= "<0900> <09ff> <0900>\n";
11427                 $stream .= "<0a00> <0aff> <0a00>\n";
11428                 $stream .= "<0b00> <0bff> <0b00>\n";
11429                 $stream .= "<0c00> <0cff> <0c00>\n";
11430                 $stream .= "<0d00> <0dff> <0d00>\n";
11431                 $stream .= "<0e00> <0eff> <0e00>\n";
11432                 $stream .= "<0f00> <0fff> <0f00>\n";
11433                 $stream .= "<1000> <10ff> <1000>\n";
11434                 $stream .= "<1100> <11ff> <1100>\n";
11435                 $stream .= "<1200> <12ff> <1200>\n";
11436                 $stream .= "<1300> <13ff> <1300>\n";
11437                 $stream .= "<1400> <14ff> <1400>\n";
11438                 $stream .= "<1500> <15ff> <1500>\n";
11439                 $stream .= "<1600> <16ff> <1600>\n";
11440                 $stream .= "<1700> <17ff> <1700>\n";
11441                 $stream .= "<1800> <18ff> <1800>\n";
11442                 $stream .= "<1900> <19ff> <1900>\n";
11443                 $stream .= "<1a00> <1aff> <1a00>\n";
11444                 $stream .= "<1b00> <1bff> <1b00>\n";
11445                 $stream .= "<1c00> <1cff> <1c00>\n";
11446                 $stream .= "<1d00> <1dff> <1d00>\n";
11447                 $stream .= "<1e00> <1eff> <1e00>\n";
11448                 $stream .= "<1f00> <1fff> <1f00>\n";
11449                 $stream .= "<2000> <20ff> <2000>\n";
11450                 $stream .= "<2100> <21ff> <2100>\n";
11451                 $stream .= "<2200> <22ff> <2200>\n";
11452                 $stream .= "<2300> <23ff> <2300>\n";
11453                 $stream .= "<2400> <24ff> <2400>\n";
11454                 $stream .= "<2500> <25ff> <2500>\n";
11455                 $stream .= "<2600> <26ff> <2600>\n";
11456                 $stream .= "<2700> <27ff> <2700>\n";
11457                 $stream .= "<2800> <28ff> <2800>\n";
11458                 $stream .= "<2900> <29ff> <2900>\n";
11459                 $stream .= "<2a00> <2aff> <2a00>\n";
11460                 $stream .= "<2b00> <2bff> <2b00>\n";
11461                 $stream .= "<2c00> <2cff> <2c00>\n";
11462                 $stream .= "<2d00> <2dff> <2d00>\n";
11463                 $stream .= "<2e00> <2eff> <2e00>\n";
11464                 $stream .= "<2f00> <2fff> <2f00>\n";
11465                 $stream .= "<3000> <30ff> <3000>\n";
11466                 $stream .= "<3100> <31ff> <3100>\n";
11467                 $stream .= "<3200> <32ff> <3200>\n";
11468                 $stream .= "<3300> <33ff> <3300>\n";
11469                 $stream .= "<3400> <34ff> <3400>\n";
11470                 $stream .= "<3500> <35ff> <3500>\n";
11471                 $stream .= "<3600> <36ff> <3600>\n";
11472                 $stream .= "<3700> <37ff> <3700>\n";
11473                 $stream .= "<3800> <38ff> <3800>\n";
11474                 $stream .= "<3900> <39ff> <3900>\n";
11475                 $stream .= "<3a00> <3aff> <3a00>\n";
11476                 $stream .= "<3b00> <3bff> <3b00>\n";
11477                 $stream .= "<3c00> <3cff> <3c00>\n";
11478                 $stream .= "<3d00> <3dff> <3d00>\n";
11479                 $stream .= "<3e00> <3eff> <3e00>\n";
11480                 $stream .= "<3f00> <3fff> <3f00>\n";
11481                 $stream .= "<4000> <40ff> <4000>\n";
11482                 $stream .= "<4100> <41ff> <4100>\n";
11483                 $stream .= "<4200> <42ff> <4200>\n";
11484                 $stream .= "<4300> <43ff> <4300>\n";
11485                 $stream .= "<4400> <44ff> <4400>\n";
11486                 $stream .= "<4500> <45ff> <4500>\n";
11487                 $stream .= "<4600> <46ff> <4600>\n";
11488                 $stream .= "<4700> <47ff> <4700>\n";
11489                 $stream .= "<4800> <48ff> <4800>\n";
11490                 $stream .= "<4900> <49ff> <4900>\n";
11491                 $stream .= "<4a00> <4aff> <4a00>\n";
11492                 $stream .= "<4b00> <4bff> <4b00>\n";
11493                 $stream .= "<4c00> <4cff> <4c00>\n";
11494                 $stream .= "<4d00> <4dff> <4d00>\n";
11495                 $stream .= "<4e00> <4eff> <4e00>\n";
11496                 $stream .= "<4f00> <4fff> <4f00>\n";
11497                 $stream .= "<5000> <50ff> <5000>\n";
11498                 $stream .= "<5100> <51ff> <5100>\n";
11499                 $stream .= "<5200> <52ff> <5200>\n";
11500                 $stream .= "<5300> <53ff> <5300>\n";
11501                 $stream .= "<5400> <54ff> <5400>\n";
11502                 $stream .= "<5500> <55ff> <5500>\n";
11503                 $stream .= "<5600> <56ff> <5600>\n";
11504                 $stream .= "<5700> <57ff> <5700>\n";
11505                 $stream .= "<5800> <58ff> <5800>\n";
11506                 $stream .= "<5900> <59ff> <5900>\n";
11507                 $stream .= "<5a00> <5aff> <5a00>\n";
11508                 $stream .= "<5b00> <5bff> <5b00>\n";
11509                 $stream .= "<5c00> <5cff> <5c00>\n";
11510                 $stream .= "<5d00> <5dff> <5d00>\n";
11511                 $stream .= "<5e00> <5eff> <5e00>\n";
11512                 $stream .= "<5f00> <5fff> <5f00>\n";
11513                 $stream .= "<6000> <60ff> <6000>\n";
11514                 $stream .= "<6100> <61ff> <6100>\n";
11515                 $stream .= "<6200> <62ff> <6200>\n";
11516                 $stream .= "<6300> <63ff> <6300>\n";
11517                 $stream .= "endbfrange\n";
11518                 $stream .= "100 beginbfrange\n";
11519                 $stream .= "<6400> <64ff> <6400>\n";
11520                 $stream .= "<6500> <65ff> <6500>\n";
11521                 $stream .= "<6600> <66ff> <6600>\n";
11522                 $stream .= "<6700> <67ff> <6700>\n";
11523                 $stream .= "<6800> <68ff> <6800>\n";
11524                 $stream .= "<6900> <69ff> <6900>\n";
11525                 $stream .= "<6a00> <6aff> <6a00>\n";
11526                 $stream .= "<6b00> <6bff> <6b00>\n";
11527                 $stream .= "<6c00> <6cff> <6c00>\n";
11528                 $stream .= "<6d00> <6dff> <6d00>\n";
11529                 $stream .= "<6e00> <6eff> <6e00>\n";
11530                 $stream .= "<6f00> <6fff> <6f00>\n";
11531                 $stream .= "<7000> <70ff> <7000>\n";
11532                 $stream .= "<7100> <71ff> <7100>\n";
11533                 $stream .= "<7200> <72ff> <7200>\n";
11534                 $stream .= "<7300> <73ff> <7300>\n";
11535                 $stream .= "<7400> <74ff> <7400>\n";
11536                 $stream .= "<7500> <75ff> <7500>\n";
11537                 $stream .= "<7600> <76ff> <7600>\n";
11538                 $stream .= "<7700> <77ff> <7700>\n";
11539                 $stream .= "<7800> <78ff> <7800>\n";
11540                 $stream .= "<7900> <79ff> <7900>\n";
11541                 $stream .= "<7a00> <7aff> <7a00>\n";
11542                 $stream .= "<7b00> <7bff> <7b00>\n";
11543                 $stream .= "<7c00> <7cff> <7c00>\n";
11544                 $stream .= "<7d00> <7dff> <7d00>\n";
11545                 $stream .= "<7e00> <7eff> <7e00>\n";
11546                 $stream .= "<7f00> <7fff> <7f00>\n";
11547                 $stream .= "<8000> <80ff> <8000>\n";
11548                 $stream .= "<8100> <81ff> <8100>\n";
11549                 $stream .= "<8200> <82ff> <8200>\n";
11550                 $stream .= "<8300> <83ff> <8300>\n";
11551                 $stream .= "<8400> <84ff> <8400>\n";
11552                 $stream .= "<8500> <85ff> <8500>\n";
11553                 $stream .= "<8600> <86ff> <8600>\n";
11554                 $stream .= "<8700> <87ff> <8700>\n";
11555                 $stream .= "<8800> <88ff> <8800>\n";
11556                 $stream .= "<8900> <89ff> <8900>\n";
11557                 $stream .= "<8a00> <8aff> <8a00>\n";
11558                 $stream .= "<8b00> <8bff> <8b00>\n";
11559                 $stream .= "<8c00> <8cff> <8c00>\n";
11560                 $stream .= "<8d00> <8dff> <8d00>\n";
11561                 $stream .= "<8e00> <8eff> <8e00>\n";
11562                 $stream .= "<8f00> <8fff> <8f00>\n";
11563                 $stream .= "<9000> <90ff> <9000>\n";
11564                 $stream .= "<9100> <91ff> <9100>\n";
11565                 $stream .= "<9200> <92ff> <9200>\n";
11566                 $stream .= "<9300> <93ff> <9300>\n";
11567                 $stream .= "<9400> <94ff> <9400>\n";
11568                 $stream .= "<9500> <95ff> <9500>\n";
11569                 $stream .= "<9600> <96ff> <9600>\n";
11570                 $stream .= "<9700> <97ff> <9700>\n";
11571                 $stream .= "<9800> <98ff> <9800>\n";
11572                 $stream .= "<9900> <99ff> <9900>\n";
11573                 $stream .= "<9a00> <9aff> <9a00>\n";
11574                 $stream .= "<9b00> <9bff> <9b00>\n";
11575                 $stream .= "<9c00> <9cff> <9c00>\n";
11576                 $stream .= "<9d00> <9dff> <9d00>\n";
11577                 $stream .= "<9e00> <9eff> <9e00>\n";
11578                 $stream .= "<9f00> <9fff> <9f00>\n";
11579                 $stream .= "<a000> <a0ff> <a000>\n";
11580                 $stream .= "<a100> <a1ff> <a100>\n";
11581                 $stream .= "<a200> <a2ff> <a200>\n";
11582                 $stream .= "<a300> <a3ff> <a300>\n";
11583                 $stream .= "<a400> <a4ff> <a400>\n";
11584                 $stream .= "<a500> <a5ff> <a500>\n";
11585                 $stream .= "<a600> <a6ff> <a600>\n";
11586                 $stream .= "<a700> <a7ff> <a700>\n";
11587                 $stream .= "<a800> <a8ff> <a800>\n";
11588                 $stream .= "<a900> <a9ff> <a900>\n";
11589                 $stream .= "<aa00> <aaff> <aa00>\n";
11590                 $stream .= "<ab00> <abff> <ab00>\n";
11591                 $stream .= "<ac00> <acff> <ac00>\n";
11592                 $stream .= "<ad00> <adff> <ad00>\n";
11593                 $stream .= "<ae00> <aeff> <ae00>\n";
11594                 $stream .= "<af00> <afff> <af00>\n";
11595                 $stream .= "<b000> <b0ff> <b000>\n";
11596                 $stream .= "<b100> <b1ff> <b100>\n";
11597                 $stream .= "<b200> <b2ff> <b200>\n";
11598                 $stream .= "<b300> <b3ff> <b300>\n";
11599                 $stream .= "<b400> <b4ff> <b400>\n";
11600                 $stream .= "<b500> <b5ff> <b500>\n";
11601                 $stream .= "<b600> <b6ff> <b600>\n";
11602                 $stream .= "<b700> <b7ff> <b700>\n";
11603                 $stream .= "<b800> <b8ff> <b800>\n";
11604                 $stream .= "<b900> <b9ff> <b900>\n";
11605                 $stream .= "<ba00> <baff> <ba00>\n";
11606                 $stream .= "<bb00> <bbff> <bb00>\n";
11607                 $stream .= "<bc00> <bcff> <bc00>\n";
11608                 $stream .= "<bd00> <bdff> <bd00>\n";
11609                 $stream .= "<be00> <beff> <be00>\n";
11610                 $stream .= "<bf00> <bfff> <bf00>\n";
11611                 $stream .= "<c000> <c0ff> <c000>\n";
11612                 $stream .= "<c100> <c1ff> <c100>\n";
11613                 $stream .= "<c200> <c2ff> <c200>\n";
11614                 $stream .= "<c300> <c3ff> <c300>\n";
11615                 $stream .= "<c400> <c4ff> <c400>\n";
11616                 $stream .= "<c500> <c5ff> <c500>\n";
11617                 $stream .= "<c600> <c6ff> <c600>\n";
11618                 $stream .= "<c700> <c7ff> <c700>\n";
11619                 $stream .= "endbfrange\n";
11620                 $stream .= "56 beginbfrange\n";
11621                 $stream .= "<c800> <c8ff> <c800>\n";
11622                 $stream .= "<c900> <c9ff> <c900>\n";
11623                 $stream .= "<ca00> <caff> <ca00>\n";
11624                 $stream .= "<cb00> <cbff> <cb00>\n";
11625                 $stream .= "<cc00> <ccff> <cc00>\n";
11626                 $stream .= "<cd00> <cdff> <cd00>\n";
11627                 $stream .= "<ce00> <ceff> <ce00>\n";
11628                 $stream .= "<cf00> <cfff> <cf00>\n";
11629                 $stream .= "<d000> <d0ff> <d000>\n";
11630                 $stream .= "<d100> <d1ff> <d100>\n";
11631                 $stream .= "<d200> <d2ff> <d200>\n";
11632                 $stream .= "<d300> <d3ff> <d300>\n";
11633                 $stream .= "<d400> <d4ff> <d400>\n";
11634                 $stream .= "<d500> <d5ff> <d500>\n";
11635                 $stream .= "<d600> <d6ff> <d600>\n";
11636                 $stream .= "<d700> <d7ff> <d700>\n";
11637                 $stream .= "<d800> <d8ff> <d800>\n";
11638                 $stream .= "<d900> <d9ff> <d900>\n";
11639                 $stream .= "<da00> <daff> <da00>\n";
11640                 $stream .= "<db00> <dbff> <db00>\n";
11641                 $stream .= "<dc00> <dcff> <dc00>\n";
11642                 $stream .= "<dd00> <ddff> <dd00>\n";
11643                 $stream .= "<de00> <deff> <de00>\n";
11644                 $stream .= "<df00> <dfff> <df00>\n";
11645                 $stream .= "<e000> <e0ff> <e000>\n";
11646                 $stream .= "<e100> <e1ff> <e100>\n";
11647                 $stream .= "<e200> <e2ff> <e200>\n";
11648                 $stream .= "<e300> <e3ff> <e300>\n";
11649                 $stream .= "<e400> <e4ff> <e400>\n";
11650                 $stream .= "<e500> <e5ff> <e500>\n";
11651                 $stream .= "<e600> <e6ff> <e600>\n";
11652                 $stream .= "<e700> <e7ff> <e700>\n";
11653                 $stream .= "<e800> <e8ff> <e800>\n";
11654                 $stream .= "<e900> <e9ff> <e900>\n";
11655                 $stream .= "<ea00> <eaff> <ea00>\n";
11656                 $stream .= "<eb00> <ebff> <eb00>\n";
11657                 $stream .= "<ec00> <ecff> <ec00>\n";
11658                 $stream .= "<ed00> <edff> <ed00>\n";
11659                 $stream .= "<ee00> <eeff> <ee00>\n";
11660                 $stream .= "<ef00> <efff> <ef00>\n";
11661                 $stream .= "<f000> <f0ff> <f000>\n";
11662                 $stream .= "<f100> <f1ff> <f100>\n";
11663                 $stream .= "<f200> <f2ff> <f200>\n";
11664                 $stream .= "<f300> <f3ff> <f300>\n";
11665                 $stream .= "<f400> <f4ff> <f400>\n";
11666                 $stream .= "<f500> <f5ff> <f500>\n";
11667                 $stream .= "<f600> <f6ff> <f600>\n";
11668                 $stream .= "<f700> <f7ff> <f700>\n";
11669                 $stream .= "<f800> <f8ff> <f800>\n";
11670                 $stream .= "<f900> <f9ff> <f900>\n";
11671                 $stream .= "<fa00> <faff> <fa00>\n";
11672                 $stream .= "<fb00> <fbff> <fb00>\n";
11673                 $stream .= "<fc00> <fcff> <fc00>\n";
11674                 $stream .= "<fd00> <fdff> <fd00>\n";
11675                 $stream .= "<fe00> <feff> <fe00>\n";
11676                 $stream .= "<ff00> <ffff> <ff00>\n";
11677                 $stream .= "endbfrange\n";
11678                 $stream .= "endcmap\n";
11679                 $stream .= "CMapName currentdict /CMap defineresource pop\n";
11680                 $stream .= "end\n";
11681                 $stream .= "end";
11682                 // ToUnicode Object
11683                 $this->_newobj();
11684                 $stream = ($this->compress) ? gzcompress($stream) : $stream;
11685                 $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
11686                 $stream = $this->_getrawstream($stream);
11687                 $this->_out('<<'.$filter.'/Length '.strlen($stream).'>> stream'."\n".$stream."\n".'endstream'."\n".'endobj');
11688                 // CIDFontType2
11689                 // A CIDFont whose glyph descriptions are based on TrueType font technology
11690                 $oid = $this->_newobj();
11691                 $out = '<< /Type /Font';
11692                 $out .= ' /Subtype /CIDFontType2';
11693                 $out .= ' /BaseFont /'.$fontname;
11694                 // A dictionary containing entries that define the character collection of the CIDFont.
11695                 $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
11696                 $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
11697                 $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
11698                 $out .= ' /CIDSystemInfo << '.$cidinfo.' >>';
11699                 $out .= ' /FontDescriptor '.($this->n + 1).' 0 R';
11700                 $out .= ' /DW '.$font['dw']; // default width
11701                 $out .= "\n".$this->_putfontwidths($font, 0);
11702                 if (isset($font['ctg']) AND (!$this->empty_string($font['ctg']))) {
11703                         $out .= "\n".'/CIDToGIDMap '.($this->n + 2).' 0 R';
11704                 }
11705                 $out .= ' >>';
11706                 $out .= "\n".'endobj';
11707                 $this->_out($out);
11708                 // Font descriptor
11709                 // A font descriptor describing the CIDFont default metrics other than its glyph widths
11710                 $this->_newobj();
11711                 $out = '<< /Type /FontDescriptor';
11712                 $out .= ' /FontName /'.$fontname;
11713                 foreach ($font['desc'] as $key => $value) {
11714                         if (is_float($value)) {
11715                                 $value = sprintf('%.3F', $value);
11716                         }
11717                         $out .= ' /'.$key.' '.$value;
11718                 }
11719                 $fontdir = false;
11720                 if (!$this->empty_string($font['file'])) {
11721                         // A stream containing a TrueType font
11722                         $out .= ' /FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R';
11723                         $fontdir = $this->FontFiles[$font['file']]['fontdir'];
11724                 }
11725                 $out .= ' >>';
11726                 $out .= "\n".'endobj';
11727                 $this->_out($out);
11728                 if (isset($font['ctg']) AND (!$this->empty_string($font['ctg']))) {
11729                         $this->_newobj();
11730                         // Embed CIDToGIDMap
11731                         // A specification of the mapping from CIDs to glyph indices
11732                         // search and get CTG font file to embedd
11733                         $ctgfile = strtolower($font['ctg']);
11734                         // search and get ctg font file to embedd
11735                         $fontfile = '';
11736                         // search files on various directories
11737                         if (($fontdir !== false) AND file_exists($fontdir.$ctgfile)) {
11738                                 $fontfile = $fontdir.$ctgfile;
11739                         } elseif (file_exists($this->_getfontpath().$ctgfile)) {
11740                                 $fontfile = $this->_getfontpath().$ctgfile;
11741                         } elseif (file_exists($ctgfile)) {
11742                                 $fontfile = $ctgfile;
11743                         }
11744                         if ($this->empty_string($fontfile)) {
11745                                 $this->Error('Font file not found: '.$ctgfile);
11746                         }
11747                         $stream = $this->_getrawstream(file_get_contents($fontfile));
11748                         $out = '<< /Length '.strlen($stream).'';
11749                         if (substr($fontfile, -2) == '.z') { // check file extension
11750                                 // Decompresses data encoded using the public-domain
11751                                 // zlib/deflate compression method, reproducing the
11752                                 // original text or binary data
11753                                 $out .= ' /Filter /FlateDecode';
11754                         }
11755                         $out .= ' >>';
11756                         $out .= ' stream'."\n".$stream."\n".'endstream';
11757                         $out .= "\n".'endobj';
11758                         $this->_out($out);
11759                 }
11760         }
11761 
11770         protected function _putcidfont0($font) {
11771                 $cidoffset = 0;
11772                 if (!isset($font['cw'][1])) {
11773                         $cidoffset = 31;
11774                 }
11775                 if (isset($font['cidinfo']['uni2cid'])) {
11776                         // convert unicode to cid.
11777                         $uni2cid = $font['cidinfo']['uni2cid'];
11778                         $cw = array();
11779                         foreach ($font['cw'] as $uni => $width) {
11780                                 if (isset($uni2cid[$uni])) {
11781                                         $cw[($uni2cid[$uni] + $cidoffset)] = $width;
11782                                 } elseif ($uni < 256) {
11783                                         $cw[$uni] = $width;
11784                                 } // else unknown character
11785                         }
11786                         $font = array_merge($font, array('cw' => $cw));
11787                 }
11788                 $name = $font['name'];
11789                 $enc = $font['enc'];
11790                 if ($enc) {
11791                         $longname = $name.'-'.$enc;
11792                 } else {
11793                         $longname = $name;
11794                 }
11795                 $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n";
11796                 $out .= '<</Type /Font';
11797                 $out .= ' /Subtype /Type0';
11798                 $out .= ' /BaseFont /'.$longname;
11799                 $out .= ' /Name /F'.$font['i'];
11800                 if ($enc) {
11801                         $out .= ' /Encoding /'.$enc;
11802                 }
11803                 $out .= ' /DescendantFonts ['.($this->n + 1).' 0 R]';
11804                 $out .= ' >>';
11805                 $out .= "\n".'endobj';
11806                 $this->_out($out);
11807                 $oid = $this->_newobj();
11808                 $out = '<</Type /Font';
11809                 $out .= ' /Subtype /CIDFontType0';
11810                 $out .= ' /BaseFont /'.$name;
11811                 $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
11812                 $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
11813                 $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
11814                 $out .= ' /CIDSystemInfo <<'.$cidinfo.'>>';
11815                 $out .= ' /FontDescriptor '.($this->n + 1).' 0 R';
11816                 $out .= ' /DW '.$font['dw'];
11817                 $out .= "\n".$this->_putfontwidths($font, $cidoffset);
11818                 $out .= ' >>';
11819                 $out .= "\n".'endobj';
11820                 $this->_out($out);
11821                 $this->_newobj();
11822                 $s = '<</Type /FontDescriptor /FontName /'.$name;
11823                 foreach ($font['desc'] as $k => $v) {
11824                         if ($k != 'Style') {
11825                                 if (is_float($v)) {
11826                                         $v = sprintf('%.3F', $v);
11827                                 }
11828                                 $s .= ' /'.$k.' '.$v.'';
11829                         }
11830                 }
11831                 $s .= '>>';
11832                 $s .= "\n".'endobj';
11833                 $this->_out($s);
11834         }
11835 
11840         protected function _putimages() {
11841                 $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
11842                 foreach ($this->imagekeys as $file) {
11843                         $info = $this->getImageBuffer($file);
11844                         // set object for alternate images array
11845                         if ((!$this->pdfa_mode) AND isset($info['altimgs']) AND !empty($info['altimgs'])) {
11846                                 $altoid = $this->_newobj();
11847                                 $out = '[';
11848                                 foreach ($info['altimgs'] as $altimage) {
11849                                         if (isset($this->xobjects['I'.$altimage[0]]['n'])) {
11850                                                 $out .= ' << /Image '.$this->xobjects['I'.$altimage[0]]['n'].' 0 R';
11851                                                 $out .= ' /DefaultForPrinting';
11852                                                 if ($altimage[1] === true) {
11853                                                         $out .= ' true';
11854                                                 } else {
11855                                                         $out .= ' false';
11856                                                 }
11857                                                 $out .= ' >>';
11858                                         }
11859                                 }
11860                                 $out .= ' ]';
11861                                 $out .= "\n".'endobj';
11862                                 $this->_out($out);
11863                         }
11864                         // set image object
11865                         $oid = $this->_newobj();
11866                         $this->xobjects['I'.$info['i']] = array('n' => $oid);
11867                         $this->setImageSubBuffer($file, 'n', $this->n);
11868                         $out = '<</Type /XObject';
11869                         $out .= ' /Subtype /Image';
11870                         $out .= ' /Width '.$info['w'];
11871                         $out .= ' /Height '.$info['h'];
11872                         if (array_key_exists('masked', $info)) {
11873                                 $out .= ' /SMask '.($this->n - 1).' 0 R';
11874                         }
11875                         // set color space
11876                         $icc = false;
11877                         if (isset($info['icc']) AND ($info['icc'] !== false)) {
11878                                 // ICC Colour Space
11879                                 $icc = true;
11880                                 $out .= ' /ColorSpace [/ICCBased '.($this->n + 1).' 0 R]';
11881                         } elseif ($info['cs'] == 'Indexed') {
11882                                 // Indexed Colour Space
11883                                 $out .= ' /ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n + 1).' 0 R]';
11884                         } else {
11885                                 // Device Colour Space
11886                                 $out .= ' /ColorSpace /'.$info['cs'];
11887                         }
11888                         if ($info['cs'] == 'DeviceCMYK') {
11889                                 $out .= ' /Decode [1 0 1 0 1 0 1 0]';
11890                         }
11891                         $out .= ' /BitsPerComponent '.$info['bpc'];
11892                         if (isset($altoid) AND ($altoid > 0)) {
11893                                 // reference to alternate images dictionary
11894                                 $out .= ' /Alternates '.$altoid.' 0 R';
11895                         }
11896                         if (isset($info['exurl']) AND !empty($info['exurl'])) {
11897                                 // external stream
11898                                 $out .= ' /Length 0';
11899                                 $out .= ' /F << /FS /URL /F '.$this->_datastring($info['exurl'], $oid).' >>';
11900                                 if (isset($info['f'])) {
11901                                         $out .= ' /FFilter /'.$info['f'];
11902                                 }
11903                                 $out .= ' >>';
11904                                 $out .= ' stream'."\n".'endstream';
11905                         } else {
11906                                 if (isset($info['f'])) {
11907                                         $out .= ' /Filter /'.$info['f'];
11908                                 }
11909                                 if (isset($info['parms'])) {
11910                                         $out .= ' '.$info['parms'];
11911                                 }
11912                                 if (isset($info['trns']) AND is_array($info['trns'])) {
11913                                         $trns = '';
11914                                         $count_info = count($info['trns']);
11915                                         for ($i=0; $i < $count_info; ++$i) {
11916                                                 $trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
11917                                         }
11918                                         $out .= ' /Mask ['.$trns.']';
11919                                 }
11920                                 $stream = $this->_getrawstream($info['data']);
11921                                 $out .= ' /Length '.strlen($stream).' >>';
11922                                 $out .= ' stream'."\n".$stream."\n".'endstream';
11923                         }
11924                         $out .= "\n".'endobj';
11925                         $this->_out($out);
11926                         if ($icc) {
11927                                 // ICC colour profile
11928                                 $this->_newobj();
11929                                 $icc = ($this->compress) ? gzcompress($info['icc']) : $info['icc'];
11930                                 $icc = $this->_getrawstream($icc);
11931                                 $this->_out('<</N '.$info['ch'].' /Alternate /'.$info['cs'].' '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj');
11932                         } elseif ($info['cs'] == 'Indexed') {
11933                                 // colour palette
11934                                 $this->_newobj();
11935                                 $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
11936                                 $pal = $this->_getrawstream($pal);
11937                                 $this->_out('<<'.$filter.'/Length '.strlen($pal).'>> stream'."\n".$pal."\n".'endstream'."\n".'endobj');
11938                         }
11939                 }
11940         }
11941 
11949         protected function _putxobjects() {
11950                 foreach ($this->xobjects as $key => $data) {
11951                         if (isset($data['outdata'])) {
11952                                 $stream = trim($data['outdata']);
11953                                 $out = $this->_getobj($data['n'])."\n";
11954                                 $out .= '<<';
11955                                 $out .= ' /Type /XObject';
11956                                 $out .= ' /Subtype /Form';
11957                                 $out .= ' /FormType 1';
11958                                 if ($this->compress) {
11959                                         $stream = gzcompress($stream);
11960                                         $out .= ' /Filter /FlateDecode';
11961                                 }
11962                                 $out .= sprintf(' /BBox [%.2F %.2F %.2F %.2F]', ($data['x'] * $this->k), (-$data['y'] * $this->k), (($data['w'] + $data['x']) * $this->k), (($data['h'] - $data['y']) * $this->k));
11963                                 $out .= ' /Matrix [1 0 0 1 0 0]';
11964                                 $out .= ' /Resources <<';
11965                                 $out .= ' /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
11966                                 if (!$this->pdfa_mode) {
11967                                         // transparency
11968                                         if (isset($data['extgstates']) AND !empty($data['extgstates'])) {
11969                                                 $out .= ' /ExtGState <<';
11970                                                 foreach ($data['extgstates'] as $k => $extgstate) {
11971                                                         if (isset($this->extgstates[$k]['name'])) {
11972                                                                 $out .= ' /'.$this->extgstates[$k]['name'];
11973                                                         } else {
11974                                                                 $out .= ' /GS'.$k;
11975                                                         }
11976                                                         $out .= ' '.$this->extgstates[$k]['n'].' 0 R';
11977                                                 }
11978                                                 $out .= ' >>';
11979                                         }
11980                                         if (isset($data['gradients']) AND !empty($data['gradients'])) {
11981                                                 $gp = '';
11982                                                 $gs = '';
11983                                                 foreach ($data['gradients'] as $id => $grad) {
11984                                                         // gradient patterns
11985                                                         $gp .= ' /p'.$id.' '.$this->gradients[$id]['pattern'].' 0 R';
11986                                                         // gradient shadings
11987                                                         $gs .= ' /Sh'.$id.' '.$this->gradients[$id]['id'].' 0 R';
11988                                                 }
11989                                                 $out .= ' /Pattern <<'.$gp.' >>';
11990                                                 $out .= ' /Shading <<'.$gs.' >>';
11991                                         }
11992                                 }
11993                                 // spot colors
11994                                 if (isset($data['spot_colors']) AND !empty($data['spot_colors'])) {
11995                                         $out .= ' /ColorSpace <<';
11996                                         foreach ($data['spot_colors'] as $name => $color) {
11997                                                 $out .= ' /CS'.$color['i'].' '.$this->spot_colors[$name]['n'].' 0 R';
11998                                         }
11999                                         $out .= ' >>';
12000                                 }
12001                                 // fonts
12002                                 if (!empty($data['fonts'])) {
12003                                         $out .= ' /Font <<';
12004                                         foreach ($data['fonts'] as $fontkey => $fontid) {
12005                                                 $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R';
12006                                         }
12007                                         $out .= ' >>';
12008                                 }
12009                                 // images or nested xobjects
12010                                 if (!empty($data['images']) OR !empty($data['xobjects'])) {
12011                                         $out .= ' /XObject <<';
12012                                         foreach ($data['images'] as $imgid) {
12013                                                 $out .= ' /I'.$imgid.' '.$this->xobjects['I'.$imgid]['n'].' 0 R';
12014                                         }
12015                                         foreach ($data['xobjects'] as $sub_id => $sub_objid) {
12016                                                 $out .= ' /'.$sub_id.' '.$sub_objid['n'].' 0 R';
12017                                         }
12018                                         $out .= ' >>';
12019                                 }
12020                                 $out .= ' >>'; //end resources
12021                                 if (isset($data['group']) AND ($data['group'] !== false)) {
12022                                         // set transparency group
12023                                         $out .= ' /Group << /Type /Group /S /Transparency';
12024                                         if (is_array($data['group'])) {
12025                                                 if (isset($data['group']['CS']) AND !empty($data['group']['CS'])) {
12026                                                         $out .= ' /CS /'.$data['group']['CS'];
12027                                                 }
12028                                                 if (isset($data['group']['I'])) {
12029                                                         $out .= ' /I /'.($data['group']['I']===true?'true':'false');
12030                                                 }
12031                                                 if (isset($data['group']['K'])) {
12032                                                         $out .= ' /K /'.($data['group']['K']===true?'true':'false');
12033                                                 }
12034                                         }
12035                                         $out .= ' >>';
12036                                 }
12037                                 $stream = $this->_getrawstream($stream, $data['n']);
12038                                 $out .= ' /Length '.strlen($stream);
12039                                 $out .= ' >>';
12040                                 $out .= ' stream'."\n".$stream."\n".'endstream';
12041                                 $out .= "\n".'endobj';
12042                                 $this->_out($out);
12043                         }
12044                 }
12045         }
12046 
12052         protected function _putspotcolors() {
12053                 foreach ($this->spot_colors as $name => $color) {
12054                         $this->_newobj();
12055                         $this->spot_colors[$name]['n'] = $this->n;
12056                         $out = '[/Separation /'.str_replace(' ', '#20', $name);
12057                         $out .= ' /DeviceCMYK <<';
12058                         $out .= ' /Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0]';
12059                         $out .= ' '.sprintf('/C1 [%.4F %.4F %.4F %.4F] ', ($color['c'] / 100), ($color['m'] / 100), ($color['y'] / 100), ($color['k'] / 100));
12060                         $out .= ' /FunctionType 2 /Domain [0 1] /N 1>>]';
12061                         $out .= "\n".'endobj';
12062                         $this->_out($out);
12063                 }
12064         }
12065 
12072         protected function _getxobjectdict() {
12073                 $out = '';
12074                 foreach ($this->xobjects as $id => $objid) {
12075                         $out .= ' /'.$id.' '.$objid['n'].' 0 R';
12076                 }
12077                 return $out;
12078         }
12079 
12084         protected function _putresourcedict() {
12085                 $out = $this->_getobj(2)."\n";
12086                 $out .= '<< /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
12087                 $out .= ' /Font <<';
12088                 foreach ($this->fontkeys as $fontkey) {
12089                         $font = $this->getFontBuffer($fontkey);
12090                         $out .= ' /F'.$font['i'].' '.$font['n'].' 0 R';
12091                 }
12092                 $out .= ' >>';
12093                 $out .= ' /XObject <<';
12094                 $out .= $this->_getxobjectdict();
12095                 $out .= ' >>';
12096                 // layers
12097                 if (!empty($this->pdflayers)) {
12098                         $out .= ' /Properties <<';
12099                         foreach ($this->pdflayers as $layer) {
12100                                 $out .= ' /'.$layer['layer'].' '.$layer['objid'].' 0 R';
12101                         }
12102                         $out .= ' >>';
12103                 }
12104                 if (!$this->pdfa_mode) {
12105                         // transparency
12106                         if (isset($this->extgstates) AND !empty($this->extgstates)) {
12107                                 $out .= ' /ExtGState <<';
12108                                 foreach ($this->extgstates as $k => $extgstate) {
12109                                         if (isset($extgstate['name'])) {
12110                                                 $out .= ' /'.$extgstate['name'];
12111                                         } else {
12112                                                 $out .= ' /GS'.$k;
12113                                         }
12114                                         $out .= ' '.$extgstate['n'].' 0 R';
12115                                 }
12116                                 $out .= ' >>';
12117                         }
12118                         if (isset($this->gradients) AND !empty($this->gradients)) {
12119                                 $gp = '';
12120                                 $gs = '';
12121                                 foreach ($this->gradients as $id => $grad) {
12122                                         // gradient patterns
12123                                         $gp .= ' /p'.$id.' '.$grad['pattern'].' 0 R';
12124                                         // gradient shadings
12125                                         $gs .= ' /Sh'.$id.' '.$grad['id'].' 0 R';
12126                                 }
12127                                 $out .= ' /Pattern <<'.$gp.' >>';
12128                                 $out .= ' /Shading <<'.$gs.' >>';
12129                         }
12130                 }
12131                 // spot colors
12132                 if (isset($this->spot_colors) AND !empty($this->spot_colors)) {
12133                         $out .= ' /ColorSpace <<';
12134                         foreach ($this->spot_colors as $color) {
12135                                 $out .= ' /CS'.$color['i'].' '.$color['n'].' 0 R';
12136                         }
12137                         $out .= ' >>';
12138                 }
12139                 $out .= ' >>';
12140                 $out .= "\n".'endobj';
12141                 $this->_out($out);
12142         }
12143 
12148         protected function _putresources() {
12149                 $this->_putextgstates();
12150                 $this->_putocg();
12151                 $this->_putfonts();
12152                 $this->_putimages();
12153                 $this->_putspotcolors();
12154                 $this->_putshaders();
12155                 $this->_putxobjects();
12156                 $this->_putresourcedict();
12157                 $this->_putdests();
12158                 $this->_putbookmarks();
12159                 $this->_putEmbeddedFiles();
12160                 $this->_putannotsobjs();
12161                 $this->_putjavascript();
12162                 $this->_putencryption();
12163         }
12164 
12171         protected function _putinfo() {
12172                 $oid = $this->_newobj();
12173                 $out = '<<';
12174                 // store current isunicode value
12175                 $prev_isunicode = $this->isunicode;
12176                 if ($this->docinfounicode) {
12177                         $this->isunicode = true;
12178                 }
12179                 if (!$this->empty_string($this->title)) {
12180                         // The document's title.
12181                         $out .= ' /Title '.$this->_textstring($this->title, $oid);
12182                 }
12183                 if (!$this->empty_string($this->author)) {
12184                         // The name of the person who created the document.
12185                         $out .= ' /Author '.$this->_textstring($this->author, $oid);
12186                 }
12187                 if (!$this->empty_string($this->subject)) {
12188                         // The subject of the document.
12189                         $out .= ' /Subject '.$this->_textstring($this->subject, $oid);
12190                 }
12191                 if (!$this->empty_string($this->keywords)) {
12192                         // Keywords associated with the document.
12193                         $out .= ' /Keywords '.$this->_textstring($this->keywords.' TCPDF', $oid);
12194                 }
12195                 if (!$this->empty_string($this->creator)) {
12196                         // 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.
12197                         $out .= ' /Creator '.$this->_textstring($this->creator, $oid);
12198                 }
12199                 // restore previous isunicode value
12200                 $this->isunicode = $prev_isunicode;
12201                 // default producer
12202                 $out .= ' /Producer '.$this->_textstring("\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", $oid);
12203                 // The date and time the document was created, in human-readable form
12204                 $out .= ' /CreationDate '.$this->_datestring();
12205                 // The date and time the document was most recently modified, in human-readable form
12206                 $out .= ' /ModDate '.$this->_datestring();
12207                 // A name object indicating whether the document has been modified to include trapping information
12208                 $out .= ' /Trapped /False';
12209                 $out .= ' >>';
12210                 $out .= "\n".'endobj';
12211                 $this->_out($out);
12212                 return $oid;
12213         }
12214 
12222         public function setExtraXMP($xmp) {
12223                 $this->custom_xmp = $xmp;
12224         }
12225 
12232         protected function _putXMP() {
12233                 $oid = $this->_newobj();
12234                 // store current isunicode value
12235                 $prev_isunicode = $this->isunicode;
12236                 $this->isunicode = true;
12237                 $prev_encrypted = $this->encrypted;
12238                 $this->encrypted = false;
12239                 // set XMP data
12240                 $xmp = '<?xpacket begin="'.$this->unichr(0xfeff).'" id="W5M0MpCehiHzreSzNTczkc9d"?>'."\n";
12241                 $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";
12242                 $xmp .= "\t".'<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">'."\n";
12243                 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">'."\n";
12244                 $xmp .= "\t\t\t".'<dc:format>application/pdf</dc:format>'."\n";
12245                 $xmp .= "\t\t\t".'<dc:title>'."\n";
12246                 $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n";
12247                 $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.$this->_escapeXML($this->title).'</rdf:li>'."\n";
12248                 $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n";
12249                 $xmp .= "\t\t\t".'</dc:title>'."\n";
12250                 $xmp .= "\t\t\t".'<dc:creator>'."\n";
12251                 $xmp .= "\t\t\t\t".'<rdf:Seq>'."\n";
12252                 $xmp .= "\t\t\t\t\t".'<rdf:li>'.$this->_escapeXML($this->author).'</rdf:li>'."\n";
12253                 $xmp .= "\t\t\t\t".'</rdf:Seq>'."\n";
12254                 $xmp .= "\t\t\t".'</dc:creator>'."\n";
12255                 $xmp .= "\t\t\t".'<dc:description>'."\n";
12256                 $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n";
12257                 $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.$this->_escapeXML($this->subject).'</rdf:li>'."\n";
12258                 $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n";
12259                 $xmp .= "\t\t\t".'</dc:description>'."\n";
12260                 $xmp .= "\t\t\t".'<dc:subject>'."\n";
12261                 $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n";
12262                 $xmp .= "\t\t\t\t\t".'<rdf:li>'.$this->_escapeXML($this->keywords).'</rdf:li>'."\n";
12263                 $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n";
12264                 $xmp .= "\t\t\t".'</dc:subject>'."\n";
12265                 $xmp .= "\t\t".'</rdf:Description>'."\n";
12266                 // convert date format
12267                 $docdate = substr($this->doc_date, 0, 4).'-'.substr($this->doc_date, 4, 2).'-'.substr($this->doc_date, 6, 2);
12268                 $docdate .= 'T'.substr($this->doc_date, 8, 2).':'.substr($this->doc_date, 10, 2).':'.substr($this->doc_date, 12, 2);
12269                 $docdate .= '+'.substr($this->doc_date, 15, 2).':'.substr($this->doc_date, 18, 2);
12270                 $docdate = $this->_escapeXML($docdate);
12271                 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/">'."\n";
12272                 $xmp .= "\t\t\t".'<xmp:CreateDate>'.$docdate.'</xmp:CreateDate>'."\n";
12273                 $xmp .= "\t\t\t".'<xmp:CreatorTool>TCPDF</xmp:CreatorTool>'."\n";
12274                 $xmp .= "\t\t\t".'<xmp:ModifyDate>'.$docdate.'</xmp:ModifyDate>'."\n";
12275                 $xmp .= "\t\t\t".'<xmp:MetadataDate>'.$docdate.'</xmp:MetadataDate>'."\n";
12276                 $xmp .= "\t\t".'</rdf:Description>'."\n";
12277                 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdf="http://ns.adobe.com/pdf/1.3/">'."\n";
12278                 $xmp .= "\t\t\t".'<pdf:Keywords>'.$this->_escapeXML($this->keywords).' TCPDF</pdf:Keywords>'."\n";
12279                 $xmp .= "\t\t\t".'<pdf:Producer>'.$this->_escapeXML("\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").'</pdf:Producer>'."\n";
12280                 $xmp .= "\t\t".'</rdf:Description>'."\n";
12281                 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/">'."\n";
12282                 $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);
12283                 $xmp .= "\t\t\t".'<xmpMM:DocumentID>'.$uuid.'</xmpMM:DocumentID>'."\n";
12284                 $xmp .= "\t\t\t".'<xmpMM:InstanceID>'.$uuid.'</xmpMM:InstanceID>'."\n";
12285                 $xmp .= "\t\t".'</rdf:Description>'."\n";
12286                 if ($this->pdfa_mode) {
12287                         $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/">'."\n";
12288                         $xmp .= "\t\t\t".'<pdfaid:part>1</pdfaid:part>'."\n";
12289                         $xmp .= "\t\t\t".'<pdfaid:conformance>B</pdfaid:conformance>'."\n";
12290                         $xmp .= "\t\t".'</rdf:Description>'."\n";
12291                 }
12292                 // XMP extension schemas
12293                 $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";
12294                 $xmp .= "\t\t\t".'<pdfaExtension:schemas>'."\n";
12295                 $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n";
12296                 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
12297                 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/pdf/1.3/</pdfaSchema:namespaceURI>'."\n";
12298                 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdf</pdfaSchema:prefix>'."\n";
12299                 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>Adobe PDF Schema</pdfaSchema:schema>'."\n";
12300                 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
12301                 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
12302                 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/xap/1.0/mm/</pdfaSchema:namespaceURI>'."\n";
12303                 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>xmpMM</pdfaSchema:prefix>'."\n";
12304                 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>XMP Media Management Schema</pdfaSchema:schema>'."\n";
12305                 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
12306                 $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
12307                 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
12308                 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
12309                 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>UUID based identifier for specific incarnation of a document</pdfaProperty:description>'."\n";
12310                 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>InstanceID</pdfaProperty:name>'."\n";
12311                 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>URI</pdfaProperty:valueType>'."\n";
12312                 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
12313                 $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
12314                 $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
12315                 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
12316                 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
12317                 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://www.aiim.org/pdfa/ns/id/</pdfaSchema:namespaceURI>'."\n";
12318                 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdfaid</pdfaSchema:prefix>'."\n";
12319                 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>PDF/A ID Schema</pdfaSchema:schema>'."\n";
12320                 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
12321                 $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
12322                 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
12323                 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
12324                 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Part of PDF/A standard</pdfaProperty:description>'."\n";
12325                 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>part</pdfaProperty:name>'."\n";
12326                 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Integer</pdfaProperty:valueType>'."\n";
12327                 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
12328                 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
12329                 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
12330                 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Amendment of PDF/A standard</pdfaProperty:description>'."\n";
12331                 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>amd</pdfaProperty:name>'."\n";
12332                 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n";
12333                 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
12334                 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
12335                 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
12336                 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Conformance level of PDF/A standard</pdfaProperty:description>'."\n";
12337                 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>conformance</pdfaProperty:name>'."\n";
12338                 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n";
12339                 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
12340                 $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
12341                 $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
12342                 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
12343                 $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n";
12344                 $xmp .= "\t\t\t".'</pdfaExtension:schemas>'."\n";
12345                 $xmp .= "\t\t".'</rdf:Description>'."\n";
12346                 $xmp .= "\t".'</rdf:RDF>'."\n";
12347                 $xmp .= $this->custom_xmp;
12348                 $xmp .= '</x:xmpmeta>'."\n";
12349                 $xmp .= '<?xpacket end="w"?>';
12350                 $out = '<< /Type /Metadata /Subtype /XML /Length '.strlen($xmp).' >> stream'."\n".$xmp."\n".'endstream'."\n".'endobj';
12351                 // restore previous isunicode value
12352                 $this->isunicode = $prev_isunicode;
12353                 $this->encrypted = $prev_encrypted;
12354                 $this->_out($out);
12355                 return $oid;
12356         }
12357 
12363         protected function _putcatalog() {
12364                 // put XMP
12365                 $xmpobj = $this->_putXMP();
12366                 // if required, add standard sRGB_IEC61966-2.1 blackscaled ICC colour profile
12367                 if ($this->pdfa_mode OR $this->force_srgb) {
12368                         $iccobj = $this->_newobj();
12369                         $icc = file_get_contents(dirname(__FILE__).'/sRGB.icc');
12370                         $filter = '';
12371                         if ($this->compress) {
12372                                 $filter = ' /Filter /FlateDecode';
12373                                 $icc = gzcompress($icc);
12374                         }
12375                         $icc = $this->_getrawstream($icc);
12376                         $this->_out('<</N 3 '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj');
12377                 }
12378                 // start catalog
12379                 $oid = $this->_newobj();
12380                 $out = '<< /Type /Catalog';
12381                 $out .= ' /Version /'.$this->PDFVersion;
12382                 //$out .= ' /Extensions <<>>';
12383                 $out .= ' /Pages 1 0 R';
12384                 //$out .= ' /PageLabels ' //...;
12385                 $out .= ' /Names <<';
12386                 if ((!$this->pdfa_mode) AND ((!empty($this->javascript)) OR (!empty($this->js_objects)))) {
12387                         $out .= ' /JavaScript '.($this->n_js).' 0 R';
12388                 }
12389                 $out .= ' >>';
12390                 if (!empty($this->dests)) {
12391                         $out .= ' /Dests '.$this->n_dests.' 0 R';
12392                 }
12393                 $out .= $this->_putviewerpreferences();
12394                 if (isset($this->LayoutMode) AND (!$this->empty_string($this->LayoutMode))) {
12395                         $out .= ' /PageLayout /'.$this->LayoutMode;
12396                 }
12397                 if (isset($this->PageMode) AND (!$this->empty_string($this->PageMode))) {
12398                         $out .= ' /PageMode /'.$this->PageMode;
12399                 }
12400                 if (count($this->outlines) > 0) {
12401                         $out .= ' /Outlines '.$this->OutlineRoot.' 0 R';
12402                         $out .= ' /PageMode /UseOutlines';
12403                 }
12404                 //$out .= ' /Threads []';
12405                 if ($this->ZoomMode == 'fullpage') {
12406                         $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /Fit]';
12407                 } elseif ($this->ZoomMode == 'fullwidth') {
12408                         $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /FitH null]';
12409                 } elseif ($this->ZoomMode == 'real') {
12410                         $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null 1]';
12411                 } elseif (!is_string($this->ZoomMode)) {
12412                         $out .= sprintf(' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null %.2F]', ($this->ZoomMode / 100));
12413                 }
12414                 //$out .= ' /AA <<>>';
12415                 //$out .= ' /URI <<>>';
12416                 $out .= ' /Metadata '.$xmpobj.' 0 R';
12417                 //$out .= ' /StructTreeRoot <<>>';
12418                 //$out .= ' /MarkInfo <<>>';
12419                 if (isset($this->l['a_meta_language'])) {
12420                         $out .= ' /Lang '.$this->_textstring($this->l['a_meta_language'], $oid);
12421                 }
12422                 //$out .= ' /SpiderInfo <<>>';
12423                 // set OutputIntent to sRGB IEC61966-2.1 if required
12424                 if ($this->pdfa_mode OR $this->force_srgb) {
12425                         $out .= ' /OutputIntents [<<';
12426                         $out .= ' /Type /OutputIntent';
12427                         $out .= ' /S /GTS_PDFA1';
12428                         $out .= ' /OutputCondition '.$this->_textstring('sRGB IEC61966-2.1', $oid);
12429                         $out .= ' /OutputConditionIdentifier '.$this->_textstring('sRGB IEC61966-2.1', $oid);
12430                         $out .= ' /RegistryName '.$this->_textstring('http://www.color.org', $oid);
12431                         $out .= ' /Info '.$this->_textstring('sRGB IEC61966-2.1', $oid);
12432                         $out .= ' /DestOutputProfile '.$iccobj.' 0 R';
12433                         $out .= ' >>]';
12434                 }
12435                 //$out .= ' /PieceInfo <<>>';
12436                 if (!empty($this->pdflayers)) {
12437                         $lyrobjs = '';
12438                         $lyrobjs_print = '';
12439                         $lyrobjs_view = '';
12440                         foreach ($this->pdflayers as $layer) {
12441                                 $lyrobjs .= ' '.$layer['objid'].' 0 R';
12442                                 if ($layer['print']) {
12443                                         $lyrobjs_print .= ' '.$layer['objid'].' 0 R';
12444                                 }
12445                                 if ($layer['view']) {
12446                                         $lyrobjs_view .= ' '.$layer['objid'].' 0 R';
12447                                 }
12448                         }
12449                         $out .= ' /OCProperties << /OCGs ['.$lyrobjs.']';
12450                         $out .= ' /D <<';
12451                         $out .= ' /Name '.$this->_textstring('Layers', $oid);
12452                         $out .= ' /Creator '.$this->_textstring('TCPDF', $oid);
12453                         $out .= ' /BaseState /ON';
12454                         $out .= ' /ON ['.$lyrobjs_print.']';
12455                         $out .= ' /OFF ['.$lyrobjs_view.']';
12456                         $out .= ' /Intent /View';
12457                         $out .= ' /AS [';
12458                         $out .= ' << /Event /Print /OCGs ['.$lyrobjs.'] /Category [/Print] >>';
12459                         $out .= ' << /Event /View /OCGs ['.$lyrobjs.'] /Category [/View] >>';
12460                         $out .= ' ]';
12461                         $out .= ' /Order ['.$lyrobjs.']';
12462                         $out .= ' /ListMode /AllPages';
12463                         //$out .= ' /RBGroups ['..']';
12464                         //$out .= ' /Locked ['..']';
12465                         $out .= ' >>';
12466                         $out .= ' >>';
12467                 }
12468                 // AcroForm
12469                 if (!empty($this->form_obj_id) OR ($this->sign AND isset($this->signature_data['cert_type']))) {
12470                         $out .= ' /AcroForm <<';
12471                         $objrefs = '';
12472                         if ($this->sign AND isset($this->signature_data['cert_type'])) {
12473                                 // set reference for signature object
12474                                 $objrefs .= $this->sig_obj_id.' 0 R';
12475                         }
12476                         if (!empty($this->empty_signature_appearance)) {
12477                                 foreach ($this->empty_signature_appearance as $esa) {
12478                                         // set reference for empty signature objects
12479                                         $objrefs .= ' '.$esa['objid'].' 0 R';
12480                                 }
12481                         }
12482                         if (!empty($this->form_obj_id)) {
12483                                 foreach($this->form_obj_id as $objid) {
12484                                         $objrefs .= ' '.$objid.' 0 R';
12485                                 }
12486                         }
12487                         $out .= ' /Fields ['.$objrefs.']';
12488                         // It's better to turn off this value and set the appearance stream for each annotation (/AP) to avoid conflicts with signature fields.
12489                         $out .= ' /NeedAppearances false';
12490                         if ($this->sign AND isset($this->signature_data['cert_type'])) {
12491                                 if ($this->signature_data['cert_type'] > 0) {
12492                                         $out .= ' /SigFlags 3';
12493                                 } else {
12494                                         $out .= ' /SigFlags 1';
12495                                 }
12496                         }
12497                         //$out .= ' /CO ';
12498                         if (isset($this->annotation_fonts) AND !empty($this->annotation_fonts)) {
12499                                 $out .= ' /DR <<';
12500                                 $out .= ' /Font <<';
12501                                 foreach ($this->annotation_fonts as $fontkey => $fontid) {
12502                                         $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R';
12503                                 }
12504                                 $out .= ' >> >>';
12505                         }
12506                         $font = $this->getFontBuffer('helvetica');
12507                         $out .= ' /DA (/F'.$font['i'].' 0 Tf 0 g)';
12508                         $out .= ' /Q '.(($this->rtl)?'2':'0');
12509                         //$out .= ' /XFA ';
12510                         $out .= ' >>';
12511                         // signatures
12512                         if ($this->sign AND isset($this->signature_data['cert_type'])) {
12513                                 if ($this->signature_data['cert_type'] > 0) {
12514                                         $out .= ' /Perms << /DocMDP '.($this->sig_obj_id + 1).' 0 R >>';
12515                                 } else {
12516                                         $out .= ' /Perms << /UR3 '.($this->sig_obj_id + 1).' 0 R >>';
12517                                 }
12518                         }
12519                 }
12520                 //$out .= ' /Legal <<>>';
12521                 //$out .= ' /Requirements []';
12522                 //$out .= ' /Collection <<>>';
12523                 //$out .= ' /NeedsRendering true';
12524                 $out .= ' >>';
12525                 $out .= "\n".'endobj';
12526                 $this->_out($out);
12527                 return $oid;
12528         }
12529 
12537         protected function _putviewerpreferences() {
12538                 $out = ' /ViewerPreferences <<';
12539                 if ($this->rtl) {
12540                         $out .= ' /Direction /R2L';
12541                 } else {
12542                         $out .= ' /Direction /L2R';
12543                 }
12544                 if (isset($this->viewer_preferences['HideToolbar']) AND ($this->viewer_preferences['HideToolbar'])) {
12545                         $out .= ' /HideToolbar true';
12546                 }
12547                 if (isset($this->viewer_preferences['HideMenubar']) AND ($this->viewer_preferences['HideMenubar'])) {
12548                         $out .= ' /HideMenubar true';
12549                 }
12550                 if (isset($this->viewer_preferences['HideWindowUI']) AND ($this->viewer_preferences['HideWindowUI'])) {
12551                         $out .= ' /HideWindowUI true';
12552                 }
12553                 if (isset($this->viewer_preferences['FitWindow']) AND ($this->viewer_preferences['FitWindow'])) {
12554                         $out .= ' /FitWindow true';
12555                 }
12556                 if (isset($this->viewer_preferences['CenterWindow']) AND ($this->viewer_preferences['CenterWindow'])) {
12557                         $out .= ' /CenterWindow true';
12558                 }
12559                 if (isset($this->viewer_preferences['DisplayDocTitle']) AND ($this->viewer_preferences['DisplayDocTitle'])) {
12560                         $out .= ' /DisplayDocTitle true';
12561                 }
12562                 if (isset($this->viewer_preferences['NonFullScreenPageMode'])) {
12563                         $out .= ' /NonFullScreenPageMode /'.$this->viewer_preferences['NonFullScreenPageMode'];
12564                 }
12565                 if (isset($this->viewer_preferences['ViewArea'])) {
12566                         $out .= ' /ViewArea /'.$this->viewer_preferences['ViewArea'];
12567                 }
12568                 if (isset($this->viewer_preferences['ViewClip'])) {
12569                         $out .= ' /ViewClip /'.$this->viewer_preferences['ViewClip'];
12570                 }
12571                 if (isset($this->viewer_preferences['PrintArea'])) {
12572                         $out .= ' /PrintArea /'.$this->viewer_preferences['PrintArea'];
12573                 }
12574                 if (isset($this->viewer_preferences['PrintClip'])) {
12575                         $out .= ' /PrintClip /'.$this->viewer_preferences['PrintClip'];
12576                 }
12577                 if (isset($this->viewer_preferences['PrintScaling'])) {
12578                         $out .= ' /PrintScaling /'.$this->viewer_preferences['PrintScaling'];
12579                 }
12580                 if (isset($this->viewer_preferences['Duplex']) AND (!$this->empty_string($this->viewer_preferences['Duplex']))) {
12581                         $out .= ' /Duplex /'.$this->viewer_preferences['Duplex'];
12582                 }
12583                 if (isset($this->viewer_preferences['PickTrayByPDFSize'])) {
12584                         if ($this->viewer_preferences['PickTrayByPDFSize']) {
12585                                 $out .= ' /PickTrayByPDFSize true';
12586                         } else {
12587                                 $out .= ' /PickTrayByPDFSize false';
12588                         }
12589                 }
12590                 if (isset($this->viewer_preferences['PrintPageRange'])) {
12591                         $PrintPageRangeNum = '';
12592                         foreach ($this->viewer_preferences['PrintPageRange'] as $k => $v) {
12593                                 $PrintPageRangeNum .= ' '.($v - 1).'';
12594                         }
12595                         $out .= ' /PrintPageRange ['.substr($PrintPageRangeNum,1).']';
12596                 }
12597                 if (isset($this->viewer_preferences['NumCopies'])) {
12598                         $out .= ' /NumCopies '.intval($this->viewer_preferences['NumCopies']);
12599                 }
12600                 $out .= ' >>';
12601                 return $out;
12602         }
12603 
12608         protected function _putheader() {
12609                 $this->_out('%PDF-'.$this->PDFVersion);
12610                 $this->_out('%'.chr(0xe2).chr(0xe3).chr(0xcf).chr(0xd3));
12611         }
12612 
12617         protected function _enddoc() {
12618                 $this->state = 1;
12619                 $this->_putheader();
12620                 $this->_putpages();
12621                 $this->_putresources();
12622                 // empty signature fields
12623                 if (!empty($this->empty_signature_appearance)) {
12624                         foreach ($this->empty_signature_appearance as $key => $esa) {
12625                                 // widget annotation for empty signature
12626                                 $out = $this->_getobj($esa['objid'])."\n";
12627                                 $out .= '<< /Type /Annot';
12628                                 $out .= ' /Subtype /Widget';
12629                                 $out .= ' /Rect ['.$esa['rect'].']';
12630                                 $out .= ' /P '.$this->page_obj_id[($esa['page'])].' 0 R'; // link to signature appearance page
12631                                 $out .= ' /F 4';
12632                                 $out .= ' /FT /Sig';
12633                                 $signame = sprintf('Signature_%03d', ($key + 1));
12634                                 $out .= ' /T '.$this->_textstring($signame, $esa['objid']);
12635                                 $out .= ' /Ff 0';
12636                                 $out .= ' >>';
12637                                 $out .= "\n".'endobj';
12638                                 $this->_out($out);
12639                         }
12640                 }
12641                 // Signature
12642                 if ($this->sign AND isset($this->signature_data['cert_type'])) {
12643                         // widget annotation for signature
12644                         $out = $this->_getobj($this->sig_obj_id)."\n";
12645                         $out .= '<< /Type /Annot';
12646                         $out .= ' /Subtype /Widget';
12647                         $out .= ' /Rect ['.$this->signature_appearance['rect'].']';
12648                         $out .= ' /P '.$this->page_obj_id[($this->signature_appearance['page'])].' 0 R'; // link to signature appearance page
12649                         $out .= ' /F 4';
12650                         $out .= ' /FT /Sig';
12651                         $out .= ' /T '.$this->_textstring('Signature_000', $this->sig_obj_id);
12652                         $out .= ' /Ff 0';
12653                         $out .= ' /V '.($this->sig_obj_id + 1).' 0 R';
12654                         $out .= ' >>';
12655                         $out .= "\n".'endobj';
12656                         $this->_out($out);
12657                         // signature
12658                         $this->_putsignature();
12659                 }
12660                 // Info
12661                 $objid_info = $this->_putinfo();
12662                 // Catalog
12663                 $objid_catalog = $this->_putcatalog();
12664                 // Cross-ref
12665                 $o = $this->bufferlen;
12666                 // XREF section
12667                 $this->_out('xref');
12668                 $this->_out('0 '.($this->n + 1));
12669                 $this->_out('0000000000 65535 f ');
12670                 for ($i=1; $i <= $this->n; ++$i) {
12671                         if (!isset($this->offsets[$i]) AND ($i > 1)) {
12672                                 $this->offsets[$i] = $this->offsets[($i - 1)];
12673                         }
12674                         $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
12675                 }
12676                 // TRAILER
12677                 $out = 'trailer'."\n";
12678                 $out .= '<<';
12679                 $out .= ' /Size '.($this->n + 1);
12680                 $out .= ' /Root '.$objid_catalog.' 0 R';
12681                 $out .= ' /Info '.$objid_info.' 0 R';
12682                 if ($this->encrypted) {
12683                         $out .= ' /Encrypt '.$this->encryptdata['objid'].' 0 R';
12684                 }
12685                 $out .= ' /ID [ <'.$this->file_id.'> <'.$this->file_id.'> ]';
12686                 $out .= ' >>';
12687                 $this->_out($out);
12688                 $this->_out('startxref');
12689                 $this->_out($o);
12690                 $this->_out('%%EOF');
12691                 $this->state = 3; // end-of-doc
12692                 if ($this->diskcache) {
12693                         // remove temporary files used for images
12694                         foreach ($this->imagekeys as $key) {
12695                                 // remove temporary files
12696                                 unlink($this->images[$key]);
12697                         }
12698                         foreach ($this->fontkeys as $key) {
12699                                 // remove temporary files
12700                                 unlink($this->fonts[$key]);
12701                         }
12702                 }
12703         }
12704 
12712         protected function _beginpage($orientation='', $format='') {
12713                 ++$this->page;
12714                 $this->setPageBuffer($this->page, '');
12715                 // initialize array for graphics tranformation positions inside a page buffer
12716                 $this->transfmrk[$this->page] = array();
12717                 $this->state = 2;
12718                 if ($this->empty_string($orientation)) {
12719                         if (isset($this->CurOrientation)) {
12720                                 $orientation = $this->CurOrientation;
12721                         } elseif ($this->fwPt > $this->fhPt) {
12722                                 // landscape
12723                                 $orientation = 'L';
12724                         } else {
12725                                 // portrait
12726                                 $orientation = 'P';
12727                         }
12728                 }
12729                 if ($this->empty_string($format)) {
12730                         $this->pagedim[$this->page] = $this->pagedim[($this->page - 1)];
12731                         $this->setPageOrientation($orientation);
12732                 } else {
12733                         $this->setPageFormat($format, $orientation);
12734                 }
12735                 if ($this->rtl) {
12736                         $this->x = $this->w - $this->rMargin;
12737                 } else {
12738                         $this->x = $this->lMargin;
12739                 }
12740                 $this->y = $this->tMargin;
12741                 if (isset($this->newpagegroup[$this->page])) {
12742                         // start a new group
12743                         $this->currpagegroup = $this->newpagegroup[$this->page];
12744                         $this->pagegroups[$this->currpagegroup] = 1;
12745                 } elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) {
12746                         ++$this->pagegroups[$this->currpagegroup];
12747                 }
12748         }
12749 
12754         protected function _endpage() {
12755                 $this->setVisibility('all');
12756                 $this->state = 1;
12757         }
12758 
12764         protected function _newobj() {
12765                 $this->_out($this->_getobj());
12766                 return $this->n;
12767         }
12768 
12776         protected function _getobj($objid='') {
12777                 if ($objid === '') {
12778                         ++$this->n;
12779                         $objid = $this->n;
12780                 }
12781                 $this->offsets[$objid] = $this->bufferlen;
12782                 return $objid.' 0 obj';
12783         }
12784 
12792         protected function _dounderline($x, $y, $txt) {
12793                 $w = $this->GetStringWidth($txt);
12794                 return $this->_dounderlinew($x, $y, $w);
12795         }
12796 
12805         protected function _dounderlinew($x, $y, $w) {
12806                 $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
12807                 return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew), $w * $this->k, $linew);
12808         }
12809 
12817         protected function _dolinethrough($x, $y, $txt) {
12818                 $w = $this->GetStringWidth($txt);
12819                 return $this->_dolinethroughw($x, $y, $w);
12820         }
12821 
12830         protected function _dolinethroughw($x, $y, $w) {
12831                 $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
12832                 return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew + ($this->FontSizePt / 3)), $w * $this->k, $linew);
12833         }
12834 
12843         protected function _dooverline($x, $y, $txt) {
12844                 $w = $this->GetStringWidth($txt);
12845                 return $this->_dooverlinew($x, $y, $w);
12846         }
12847 
12856         protected function _dooverlinew($x, $y, $w) {
12857                 $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
12858                 return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, (($this->h - $y + $this->FontAscent) * $this->k) - $linew, $w * $this->k, $linew);
12859 
12860         }
12861 
12868         protected function _freadint($f) {
12869                 $a = unpack('Ni', fread($f, 4));
12870                 return $a['i'];
12871         }
12872 
12879         protected function _escape($s) {
12880                 // the chr(13) substitution fixes the Bugs item #1421290.
12881                 return strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r'));
12882         }
12883 
12891         protected function _datastring($s, $n=0) {
12892                 if ($n == 0) {
12893                         $n = $this->n;
12894                 }
12895                 $s = $this->_encrypt_data($n, $s);
12896                 return '('. $this->_escape($s).')';
12897         }
12898 
12906         protected function _datestring($n=0) {
12907                 return $this->_datastring('D:'.$this->doc_date, $n);
12908         }
12909 
12917         protected function _textstring($s, $n=0) {
12918                 if ($this->isunicode) {
12919                         //Convert string to UTF-16BE
12920                         $s = $this->UTF8ToUTF16BE($s, true);
12921                 }
12922                 return $this->_datastring($s, $n);
12923         }
12924 
12933         protected function _escapetext($s) {
12934                 if ($this->isunicode) {
12935                         if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
12936                                 $s = $this->UTF8ToLatin1($s);
12937                         } else {
12938                                 //Convert string to UTF-16BE and reverse RTL language
12939                                 $s = $this->utf8StrRev($s, false, $this->tmprtl);
12940                         }
12941                 }
12942                 return $this->_escape($s);
12943         }
12944 
12952         protected function _escapeXML($str) {
12953                 $replaceTable = array("\0" => '', '&' => '&amp;', '<' => '&lt;', '>' => '&gt;');
12954                 $str = strtr($str, $replaceTable);
12955                 return $str;
12956         }
12957 
12966         protected function _getrawstream($s, $n=0) {
12967                 if ($n <= 0) {
12968                         // default to current object
12969                         $n = $this->n;
12970                 }
12971                 return $this->_encrypt_data($n, $s);
12972         }
12973 
12981         protected function _getstream($s, $n=0) {
12982                 return 'stream'."\n".$this->_getrawstream($s, $n)."\n".'endstream';
12983         }
12984 
12992         protected function _putstream($s, $n=0) {
12993                 $this->_out($this->_getstream($s, $n));
12994         }
12995 
13001         protected function _out($s) {
13002                 if ($this->state == 2) {
13003                         if ($this->inxobj) {
13004                                 // we are inside an XObject template
13005                                 $this->xobjects[$this->xobjid]['outdata'] .= $s."\n";
13006                         } elseif ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
13007                                 // puts data before page footer
13008                                 $pagebuff = $this->getPageBuffer($this->page);
13009                                 $page = substr($pagebuff, 0, -$this->footerlen[$this->page]);
13010                                 $footer = substr($pagebuff, -$this->footerlen[$this->page]);
13011                                 $this->setPageBuffer($this->page, $page.$s."\n".$footer);
13012                                 // update footer position
13013                                 $this->footerpos[$this->page] += strlen($s."\n");
13014                         } else {
13015                                 $this->setPageBuffer($this->page, $s."\n", true);
13016                         }
13017                 } else {
13018                         $this->setBuffer($s."\n");
13019                 }
13020         }
13021 
13056         protected function UTF8StringToArray($str) {
13057                 // build a unique string key
13058                 $strkey = md5($str);
13059                 if (isset($this->cache_UTF8StringToArray[$strkey])) {
13060                         // return cached value
13061                         $chrarray = $this->cache_UTF8StringToArray[$strkey]['s'];
13062                         if (!isset($this->cache_UTF8StringToArray[$strkey]['f'][$this->CurrentFont['fontkey']])) {
13063                                 if ($this->isunicode) {
13064                                         foreach ($chrarray as $chr) {
13065                                                 // store this char for font subsetting
13066                                                 $this->CurrentFont['subsetchars'][$chr] = true;
13067                                         }
13068                                         // update font subsetchars
13069                                         $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
13070                                 }
13071                                 $this->cache_UTF8StringToArray[$strkey]['f'][$this->CurrentFont['fontkey']] = true;
13072                         }
13073                         return $chrarray;
13074                 }
13075                 // check cache size
13076                 if ($this->cache_size_UTF8StringToArray >= $this->cache_maxsize_UTF8StringToArray) {
13077                         // remove first element
13078                         array_shift($this->cache_UTF8StringToArray);
13079                 }
13080                 // new cache array for selected string
13081                 $this->cache_UTF8StringToArray[$strkey] = array('s' => array(), 'f' => array());
13082                 ++$this->cache_size_UTF8StringToArray;
13083                 if (!$this->isunicode) {
13084                         // split string into array of equivalent codes
13085                         $strarr = array();
13086                         $strlen = strlen($str);
13087                         for ($i=0; $i < $strlen; ++$i) {
13088                                 $strarr[] = ord($str{$i});
13089                         }
13090                         // insert new value on cache
13091                         $this->cache_UTF8StringToArray[$strkey]['s'] = $strarr;
13092                         $this->cache_UTF8StringToArray[$strkey]['f'][$this->CurrentFont['fontkey']] = true;
13093                         return $strarr;
13094                 }
13095                 $unichar = -1; // last unicode char
13096                 $unicode = array(); // array containing unicode values
13097                 $bytes  = array(); // array containing single character byte sequences
13098                 $numbytes = 1; // number of octetc needed to represent the UTF-8 character
13099                 $str .= ''; // force $str to be a string
13100                 $length = strlen($str);
13101                 for ($i = 0; $i < $length; ++$i) {
13102                         $char = ord($str{$i}); // get one string character at time
13103                         if (count($bytes) == 0) { // get starting octect
13104                                 if ($char <= 0x7F) {
13105                                         $unichar = $char; // use the character "as is" because is ASCII
13106                                         $numbytes = 1;
13107                                 } elseif (($char >> 0x05) == 0x06) { // 2 bytes character (0x06 = 110 BIN)
13108                                         $bytes[] = ($char - 0xC0) << 0x06;
13109                                         $numbytes = 2;
13110                                 } elseif (($char >> 0x04) == 0x0E) { // 3 bytes character (0x0E = 1110 BIN)
13111                                         $bytes[] = ($char - 0xE0) << 0x0C;
13112                                         $numbytes = 3;
13113                                 } elseif (($char >> 0x03) == 0x1E) { // 4 bytes character (0x1E = 11110 BIN)
13114                                         $bytes[] = ($char - 0xF0) << 0x12;
13115                                         $numbytes = 4;
13116                                 } else {
13117                                         // use replacement character for other invalid sequences
13118                                         $unichar = 0xFFFD;
13119                                         $bytes = array();
13120                                         $numbytes = 1;
13121                                 }
13122                         } elseif (($char >> 0x06) == 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN
13123                                 $bytes[] = $char - 0x80;
13124                                 if (count($bytes) == $numbytes) {
13125                                         // compose UTF-8 bytes to a single unicode value
13126                                         $char = $bytes[0];
13127                                         for ($j = 1; $j < $numbytes; ++$j) {
13128                                                 $char += ($bytes[$j] << (($numbytes - $j - 1) * 0x06));
13129                                         }
13130                                         if ((($char >= 0xD800) AND ($char <= 0xDFFF)) OR ($char >= 0x10FFFF)) {
13131                                                 /* The definition of UTF-8 prohibits encoding character numbers between
13132                                                 U+D800 and U+DFFF, which are reserved for use with the UTF-16
13133                                                 encoding form (as surrogate pairs) and do not directly represent
13134                                                 characters. */
13135                                                 $unichar = 0xFFFD; // use replacement character
13136                                         } else {
13137                                                 $unichar = $char; // add char to array
13138                                         }
13139                                         // reset data for next char
13140                                         $bytes = array();
13141                                         $numbytes = 1;
13142                                 }
13143                         } else {
13144                                 // use replacement character for other invalid sequences
13145                                 $unichar = 0xFFFD;
13146                                 $bytes = array();
13147                                 $numbytes = 1;
13148                         }
13149                         if ($unichar >= 0) {
13150                                 // insert unicode value into array
13151                                 $unicode[] = $unichar;
13152                                 // store this char for font subsetting
13153                                 $this->CurrentFont['subsetchars'][$unichar] = true;
13154                                 $unichar = -1;
13155                         }
13156                 }
13157                 // update font subsetchars
13158                 $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
13159                 // insert new value on cache
13160                 $this->cache_UTF8StringToArray[$strkey]['s'] = $unicode;
13161                 $this->cache_UTF8StringToArray[$strkey]['f'][$this->CurrentFont['fontkey']] = true;
13162                 return $unicode;
13163         }
13164 
13175         protected function UTF8ToUTF16BE($str, $setbom=false) {
13176                 if (!$this->isunicode) {
13177                         return $str; // string is not in unicode
13178                 }
13179                 $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
13180                 return $this->arrUTF8ToUTF16BE($unicode, $setbom);
13181         }
13182 
13191         protected function UTF8ToLatin1($str) {
13192                 if (!$this->isunicode) {
13193                         return $str; // string is not in unicode
13194                 }
13195                 $outstr = ''; // string to be returned
13196                 $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
13197                 foreach ($unicode as $char) {
13198                         if ($char < 256) {
13199                                 $outstr .= chr($char);
13200                         } elseif (array_key_exists($char, $this->unicode->uni_utf8tolatin)) {
13201                                 // map from UTF-8
13202                                 $outstr .= chr($this->unicode->uni_utf8tolatin[$char]);
13203                         } elseif ($char == 0xFFFD) {
13204                                 // skip
13205                         } else {
13206                                 $outstr .= '?';
13207                         }
13208                 }
13209                 return $outstr;
13210         }
13211 
13220         protected function UTF8ArrToLatin1($unicode) {
13221                 if ((!$this->isunicode) OR $this->isUnicodeFont()) {
13222                         return $unicode;
13223                 }
13224                 $outarr = array(); // array to be returned
13225                 foreach ($unicode as $char) {
13226                         if ($char < 256) {
13227                                 $outarr[] = $char;
13228                         } elseif (array_key_exists($char, $this->unicode->uni_utf8tolatin)) {
13229                                 // map from UTF-8
13230                                 $outarr[] = $this->unicode->uni_utf8tolatin[$char];
13231                         } elseif ($char == 0xFFFD) {
13232                                 // skip
13233                         } else {
13234                                 $outarr[] = 63; // '?' character
13235                         }
13236                 }
13237                 return $outarr;
13238         }
13239 
13278         protected function arrUTF8ToUTF16BE($unicode, $setbom=false) {
13279                 $outstr = ''; // string to be returned
13280                 if ($setbom) {
13281                         $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM)
13282                 }
13283                 foreach ($unicode as $char) {
13284                         if ($char == 0x200b) {
13285                                 // skip Unicode Character 'ZERO WIDTH SPACE' (DEC:8203, U+200B)
13286                         } elseif ($char == 0xFFFD) {
13287                                 $outstr .= "\xFF\xFD"; // replacement character
13288                         } elseif ($char < 0x10000) {
13289                                 $outstr .= chr($char >> 0x08);
13290                                 $outstr .= chr($char & 0xFF);
13291                         } else {
13292                                 $char -= 0x10000;
13293                                 $w1 = 0xD800 | ($char >> 0x0a);
13294                                 $w2 = 0xDC00 | ($char & 0x3FF);
13295                                 $outstr .= chr($w1 >> 0x08);
13296                                 $outstr .= chr($w1 & 0xFF);
13297                                 $outstr .= chr($w2 >> 0x08);
13298                                 $outstr .= chr($w2 & 0xFF);
13299                         }
13300                 }
13301                 return $outstr;
13302         }
13303         // ====================================================
13304 
13311         public function setHeaderFont($font) {
13312                 $this->header_font = $font;
13313         }
13314 
13321         public function getHeaderFont() {
13322                 return $this->header_font;
13323         }
13324 
13331         public function setFooterFont($font) {
13332                 $this->footer_font = $font;
13333         }
13334 
13341         public function getFooterFont() {
13342                 return $this->footer_font;
13343         }
13344 
13351         public function setLanguageArray($language) {
13352                 $this->l = $language;
13353                 if (isset($this->l['a_meta_dir'])) {
13354                         $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
13355                 } else {
13356                         $this->rtl = false;
13357                 }
13358         }
13359 
13364         public function getPDFData() {
13365                 if ($this->state < 3) {
13366                         $this->Close();
13367                 }
13368                 return $this->buffer;
13369         }
13370 
13383         public function addHtmlLink($url, $name, $fill=false, $firstline=false, $color='', $style=-1, $firstblock=false) {
13384                 if (!$this->empty_string($url) AND ($url{0} == '#')) {
13385                         // convert url to internal link
13386                         $lnkdata = explode(',', $url);
13387                         if (isset($lnkdata[0])) {
13388                                 $page = intval(substr($lnkdata[0], 1));
13389                                 if (empty($page) OR ($page <= 0)) {
13390                                         $page = $this->page;
13391                                 }
13392                                 if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
13393                                         $lnky = floatval($lnkdata[1]);
13394                                 } else {
13395                                         $lnky = 0;
13396                                 }
13397                                 $url = $this->AddLink();
13398                                 $this->SetLink($url, $lnky, $page);
13399                         }
13400                 }
13401                 // store current settings
13402                 $prevcolor = $this->fgcolor;
13403                 $prevstyle = $this->FontStyle;
13404                 if (empty($color)) {
13405                         $this->SetTextColorArray($this->htmlLinkColorArray);
13406                 } else {
13407                         $this->SetTextColorArray($color);
13408                 }
13409                 if ($style == -1) {
13410                         $this->SetFont('', $this->FontStyle.$this->htmlLinkFontStyle);
13411                 } else {
13412                         $this->SetFont('', $this->FontStyle.$style);
13413                 }
13414                 $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline, $firstblock, 0);
13415                 // restore settings
13416                 $this->SetFont('', $prevstyle);
13417                 $this->SetTextColorArray($prevcolor);
13418                 return $ret;
13419         }
13420 
13428         public function convertHTMLColorToDec($hcolor='#FFFFFF', $defcol=array(128,128,128)) {
13429                 $color = preg_replace('/[\s]*/', '', $hcolor); // remove extra spaces
13430                 $color = strtolower($color);
13431                 if (($dotpos = strpos($color, '.')) !== false) {
13432                         // remove class parent (i.e.: color.red)
13433                         $color = substr($color, ($dotpos + 1));
13434                 }
13435                 if (strlen($color) == 0) {
13436                         return $defcol;
13437                 }
13438                 // RGB ARRAY
13439                 if (substr($color, 0, 3) == 'rgb') {
13440                         $codes = substr($color, 4);
13441                         $codes = str_replace(')', '', $codes);
13442                         $returncolor = explode(',', $codes);
13443                         foreach ($returncolor as $key => $val) {
13444                                 if (strpos($val, '%') > 0) {
13445                                         // percentage
13446                                         $returncolor[$key] = (255 * intval($val) / 100);
13447                                 } else {
13448                                         $returncolor[$key] = intval($val);
13449                                 }
13450                                 // normalize value
13451                                 $returncolor[$key] = max(0, min(255, $returncolor[$key]));
13452                         }
13453                         return $returncolor;
13454                 }
13455                 // CMYK ARRAY
13456                 if (substr($color, 0, 4) == 'cmyk') {
13457                         $codes = substr($color, 5);
13458                         $codes = str_replace(')', '', $codes);
13459                         $returncolor = explode(',', $codes);
13460                         foreach ($returncolor as $key => $val) {
13461                                 if (strpos($val, '%') !== false) {
13462                                         // percentage
13463                                         $returncolor[$key] = (100 * intval($val) / 100);
13464                                 } else {
13465                                         $returncolor[$key] = intval($val);
13466                                 }
13467                                 // normalize value
13468                                 $returncolor[$key] = max(0, min(100, $returncolor[$key]));
13469                         }
13470                         return $returncolor;
13471                 }
13472                 if ($color{0} != '#') {
13473                         // COLOR NAME
13474                         if (isset($this->webcolor[$color])) {
13475                                 // web color
13476                                 $color_code = $this->webcolor[$color];
13477                         } else {
13478                                 // spot color
13479                                 $returncolor = $this->getSpotColor($color);
13480                                 if ($returncolor === false) {
13481                                         $returncolor = $defcol;
13482                                 }
13483                                 return $returncolor;
13484                         }
13485                 } else {
13486                         $color_code = substr($color, 1);
13487                 }
13488                 // HEXADECIMAL REPRESENTATION
13489                 switch (strlen($color_code)) {
13490                         case 3: {
13491                                 // 3-digit RGB hexadecimal representation
13492                                 $r = substr($color_code, 0, 1);
13493                                 $g = substr($color_code, 1, 1);
13494                                 $b = substr($color_code, 2, 1);
13495                                 $returncolor = array();
13496                                 $returncolor['R'] = max(0, min(255, hexdec($r.$r)));
13497                                 $returncolor['G'] = max(0, min(255, hexdec($g.$g)));
13498                                 $returncolor['B'] = max(0, min(255, hexdec($b.$b)));
13499                                 break;
13500                         }
13501                         case 6: {
13502                                 // 6-digit RGB hexadecimal representation
13503                                 $returncolor = array();
13504                                 $returncolor['R'] = max(0, min(255, hexdec(substr($color_code, 0, 2))));
13505                                 $returncolor['G'] = max(0, min(255, hexdec(substr($color_code, 2, 2))));
13506                                 $returncolor['B'] = max(0, min(255, hexdec(substr($color_code, 4, 2))));
13507                                 break;
13508                         }
13509                         case 8: {
13510                                 // 8-digit CMYK hexadecimal representation
13511                                 $returncolor = array();
13512                                 $returncolor['C'] = max(0, min(100, round(hexdec(substr($color_code, 0, 2)) / 2.55)));
13513                                 $returncolor['M'] = max(0, min(100, round(hexdec(substr($color_code, 2, 2)) / 2.55)));
13514                                 $returncolor['Y'] = max(0, min(100, round(hexdec(substr($color_code, 4, 2)) / 2.55)));
13515                                 $returncolor['K'] = max(0, min(100, round(hexdec(substr($color_code, 6, 2)) / 2.55)));
13516                                 break;
13517                         }
13518                         default: {
13519                                 $returncolor = $defcol;
13520                                 break;
13521                         }
13522                 }
13523                 return $returncolor;
13524         }
13525 
13533         public function pixelsToUnits($px) {
13534                 return ($px / ($this->imgscale * $this->k));
13535         }
13536 
13544         public function unhtmlentities($text_to_convert) {
13545                 return @html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding);
13546         }
13547 
13548         // ENCRYPTION METHODS ----------------------------------
13549 
13558         protected function getRandomSeed($seed='') {
13559                 $seed .= microtime();
13560                 if (function_exists('openssl_random_pseudo_bytes') AND (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN')) {
13561                         // this is not used on windows systems because it is very slow for a know bug
13562                         $seed .= openssl_random_pseudo_bytes(512);
13563                 } else {
13564                         for ($i = 0; $i < 23; ++$i) {
13565                                 $seed .= uniqid('', true);
13566                         }
13567                 }
13568                 $seed .= uniqid('', true);
13569                 $seed .= rand();
13570                 $seed .= getmypid();
13571                 $seed .= __FILE__;
13572                 $seed .= $this->bufferlen;
13573                 if (isset($_SERVER['REMOTE_ADDR'])) {
13574                         $seed .= $_SERVER['REMOTE_ADDR'];
13575                 }
13576                 if (isset($_SERVER['HTTP_USER_AGENT'])) {
13577                         $seed .= $_SERVER['HTTP_USER_AGENT'];
13578                 }
13579                 if (isset($_SERVER['HTTP_ACCEPT'])) {
13580                         $seed .= $_SERVER['HTTP_ACCEPT'];
13581                 }
13582                 if (isset($_SERVER['HTTP_ACCEPT_ENCODING'])) {
13583                         $seed .= $_SERVER['HTTP_ACCEPT_ENCODING'];
13584                 }
13585                 if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
13586                         $seed .= $_SERVER['HTTP_ACCEPT_LANGUAGE'];
13587                 }
13588                 if (isset($_SERVER['HTTP_ACCEPT_CHARSET'])) {
13589                         $seed .= $_SERVER['HTTP_ACCEPT_CHARSET'];
13590                 }
13591                 $seed .= rand();
13592                 $seed .= uniqid('', true);
13593                 $seed .= microtime();
13594                 return $seed;
13595         }
13596 
13606         protected function _objectkey($n) {
13607                 $objkey = $this->encryptdata['key'].pack('VXxx', $n);
13608                 if ($this->encryptdata['mode'] == 2) { // AES-128
13609                         // AES padding
13610                         $objkey .= "\x73\x41\x6C\x54"; // sAlT
13611                 }
13612                 $objkey = substr($this->_md5_16($objkey), 0, (($this->encryptdata['Length'] / 8) + 5));
13613                 $objkey = substr($objkey, 0, 16);
13614                 return $objkey;
13615         }
13616 
13626         protected function _encrypt_data($n, $s) {
13627                 if (!$this->encrypted) {
13628                         return $s;
13629                 }
13630                 switch ($this->encryptdata['mode']) {
13631                         case 0:   // RC4-40
13632                         case 1: { // RC4-128
13633                                 $s = $this->_RC4($this->_objectkey($n), $s);
13634                                 break;
13635                         }
13636                         case 2: { // AES-128
13637                                 $s = $this->_AES($this->_objectkey($n), $s);
13638                                 break;
13639                         }
13640                         case 3: { // AES-256
13641                                 $s = $this->_AES($this->encryptdata['key'], $s);
13642                                 break;
13643                         }
13644                 }
13645                 return $s;
13646         }
13647 
13654         protected function _putencryption() {
13655                 if (!$this->encrypted) {
13656                         return;
13657                 }
13658                 $this->encryptdata['objid'] = $this->_newobj();
13659                 $out = '<<';
13660                 if (!isset($this->encryptdata['Filter']) OR empty($this->encryptdata['Filter'])) {
13661                         $this->encryptdata['Filter'] = 'Standard';
13662                 }
13663                 $out .= ' /Filter /'.$this->encryptdata['Filter'];
13664                 if (isset($this->encryptdata['SubFilter']) AND !empty($this->encryptdata['SubFilter'])) {
13665                         $out .= ' /SubFilter /'.$this->encryptdata['SubFilter'];
13666                 }
13667                 if (!isset($this->encryptdata['V']) OR empty($this->encryptdata['V'])) {
13668                         $this->encryptdata['V'] = 1;
13669                 }
13670                 // V is a code specifying the algorithm to be used in encrypting and decrypting the document
13671                 $out .= ' /V '.$this->encryptdata['V'];
13672                 if (isset($this->encryptdata['Length']) AND !empty($this->encryptdata['Length'])) {
13673                         // The length of the encryption key, in bits. The value shall be a multiple of 8, in the range 40 to 256
13674                         $out .= ' /Length '.$this->encryptdata['Length'];
13675                 } else {
13676                         $out .= ' /Length 40';
13677                 }
13678                 if ($this->encryptdata['V'] >= 4) {
13679                         if (!isset($this->encryptdata['StmF']) OR empty($this->encryptdata['StmF'])) {
13680                                 $this->encryptdata['StmF'] = 'Identity';
13681                         }
13682                         if (!isset($this->encryptdata['StrF']) OR empty($this->encryptdata['StrF'])) {
13683                                 // The name of the crypt filter that shall be used when decrypting all strings in the document.
13684                                 $this->encryptdata['StrF'] = 'Identity';
13685                         }
13686                         // A dictionary whose keys shall be crypt filter names and whose values shall be the corresponding crypt filter dictionaries.
13687                         if (isset($this->encryptdata['CF']) AND !empty($this->encryptdata['CF'])) {
13688                                 $out .= ' /CF <<';
13689                                 $out .= ' /'.$this->encryptdata['StmF'].' <<';
13690                                 $out .= ' /Type /CryptFilter';
13691                                 if (isset($this->encryptdata['CF']['CFM']) AND !empty($this->encryptdata['CF']['CFM'])) {
13692                                         // The method used
13693                                         $out .= ' /CFM /'.$this->encryptdata['CF']['CFM'];
13694                                         if ($this->encryptdata['pubkey']) {
13695                                                 $out .= ' /Recipients [';
13696                                                 foreach ($this->encryptdata['Recipients'] as $rec) {
13697                                                         $out .= ' <'.$rec.'>';
13698                                                 }
13699                                                 $out .= ' ]';
13700                                                 if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) {
13701                                                         $out .= ' /EncryptMetadata false';
13702                                                 } else {
13703                                                         $out .= ' /EncryptMetadata true';
13704                                                 }
13705                                         }
13706                                 } else {
13707                                         $out .= ' /CFM /None';
13708                                 }
13709                                 if (isset($this->encryptdata['CF']['AuthEvent']) AND !empty($this->encryptdata['CF']['AuthEvent'])) {
13710                                         // The event to be used to trigger the authorization that is required to access encryption keys used by this filter.
13711                                         $out .= ' /AuthEvent /'.$this->encryptdata['CF']['AuthEvent'];
13712                                 } else {
13713                                         $out .= ' /AuthEvent /DocOpen';
13714                                 }
13715                                 if (isset($this->encryptdata['CF']['Length']) AND !empty($this->encryptdata['CF']['Length'])) {
13716                                         // The bit length of the encryption key.
13717                                         $out .= ' /Length '.$this->encryptdata['CF']['Length'];
13718                                 }
13719                                 $out .= ' >> >>';
13720                         }
13721                         // The name of the crypt filter that shall be used by default when decrypting streams.
13722                         $out .= ' /StmF /'.$this->encryptdata['StmF'];
13723                         // The name of the crypt filter that shall be used when decrypting all strings in the document.
13724                         $out .= ' /StrF /'.$this->encryptdata['StrF'];
13725                         if (isset($this->encryptdata['EFF']) AND !empty($this->encryptdata['EFF'])) {
13726                                 // The name of the crypt filter that shall be used when encrypting embedded file streams that do not have their own crypt filter specifier.
13727                                 $out .= ' /EFF /'.$this->encryptdata[''];
13728                         }
13729                 }
13730                 // Additional encryption dictionary entries for the standard security handler
13731                 if ($this->encryptdata['pubkey']) {
13732                         if (($this->encryptdata['V'] < 4) AND isset($this->encryptdata['Recipients']) AND !empty($this->encryptdata['Recipients'])) {
13733                                 $out .= ' /Recipients [';
13734                                 foreach ($this->encryptdata['Recipients'] as $rec) {
13735                                         $out .= ' <'.$rec.'>';
13736                                 }
13737                                 $out .= ' ]';
13738                         }
13739                 } else {
13740                         $out .= ' /R';
13741                         if ($this->encryptdata['V'] == 5) { // AES-256
13742                                 $out .= ' 5';
13743                                 $out .= ' /OE ('.$this->_escape($this->encryptdata['OE']).')';
13744                                 $out .= ' /UE ('.$this->_escape($this->encryptdata['UE']).')';
13745                                 $out .= ' /Perms ('.$this->_escape($this->encryptdata['perms']).')';
13746                         } elseif ($this->encryptdata['V'] == 4) { // AES-128
13747                                 $out .= ' 4';
13748                         } elseif ($this->encryptdata['V'] < 2) { // RC-40
13749                                 $out .= ' 2';
13750                         } else { // RC-128
13751                                 $out .= ' 3';
13752                         }
13753                         $out .= ' /O ('.$this->_escape($this->encryptdata['O']).')';
13754                         $out .= ' /U ('.$this->_escape($this->encryptdata['U']).')';
13755                         $out .= ' /P '.$this->encryptdata['P'];
13756                         if (isset($this->encryptdata['EncryptMetadata']) AND (!$this->encryptdata['EncryptMetadata'])) {
13757                                 $out .= ' /EncryptMetadata false';
13758                         } else {
13759                                 $out .= ' /EncryptMetadata true';
13760                         }
13761                 }
13762                 $out .= ' >>';
13763                 $out .= "\n".'endobj';
13764                 $this->_out($out);
13765         }
13766 
13777         protected function _RC4($key, $text) {
13778                 if (function_exists('mcrypt_decrypt') AND ($out = @mcrypt_decrypt(MCRYPT_ARCFOUR, $key, $text, MCRYPT_MODE_STREAM, ''))) {
13779                         // try to use mcrypt function if exist
13780                         return $out;
13781                 }
13782                 if ($this->last_enc_key != $key) {
13783                         $k = str_repeat($key, ((256 / strlen($key)) + 1));
13784                         $rc4 = range(0, 255);
13785                         $j = 0;
13786                         for ($i = 0; $i < 256; ++$i) {
13787                                 $t = $rc4[$i];
13788                                 $j = ($j + $t + ord($k{$i})) % 256;
13789                                 $rc4[$i] = $rc4[$j];
13790                                 $rc4[$j] = $t;
13791                         }
13792                         $this->last_enc_key = $key;
13793                         $this->last_enc_key_c = $rc4;
13794                 } else {
13795                         $rc4 = $this->last_enc_key_c;
13796                 }
13797                 $len = strlen($text);
13798                 $a = 0;
13799                 $b = 0;
13800                 $out = '';
13801                 for ($i = 0; $i < $len; ++$i) {
13802                         $a = ($a + 1) % 256;
13803                         $t = $rc4[$a];
13804                         $b = ($b + $t) % 256;
13805                         $rc4[$a] = $rc4[$b];
13806                         $rc4[$b] = $t;
13807                         $k = $rc4[($rc4[$a] + $rc4[$b]) % 256];
13808                         $out .= chr(ord($text{$i}) ^ $k);
13809                 }
13810                 return $out;
13811         }
13812 
13823         protected function _AES($key, $text) {
13824                 // padding (RFC 2898, PKCS #5: Password-Based Cryptography Specification Version 2.0)
13825                 $padding = 16 - (strlen($text) % 16);
13826                 $text .= str_repeat(chr($padding), $padding);
13827                 $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_RAND);
13828                 $text = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $text, MCRYPT_MODE_CBC, $iv);
13829                 $text = $iv.$text;
13830                 return $text;
13831         }
13832 
13841         protected function _md5_16($str) {
13842                 return pack('H*', md5($str));
13843         }
13844 
13852         protected function _Uvalue() {
13853                 if ($this->encryptdata['mode'] == 0) { // RC4-40
13854                         return $this->_RC4($this->encryptdata['key'], $this->enc_padding);
13855                 } elseif ($this->encryptdata['mode'] < 3) { // RC4-128, AES-128
13856                         $tmp = $this->_md5_16($this->enc_padding.$this->encryptdata['fileid']);
13857                         $enc = $this->_RC4($this->encryptdata['key'], $tmp);
13858                         $len = strlen($tmp);
13859                         for ($i = 1; $i <= 19; ++$i) {
13860                                 $ek = '';
13861                                 for ($j = 0; $j < $len; ++$j) {
13862                                         $ek .= chr(ord($this->encryptdata['key']{$j}) ^ $i);
13863                                 }
13864                                 $enc = $this->_RC4($ek, $enc);
13865                         }
13866                         $enc .= str_repeat("\x00", 16);
13867                         return substr($enc, 0, 32);
13868                 } elseif ($this->encryptdata['mode'] == 3) { // AES-256
13869                         $seed = $this->_md5_16($this->getRandomSeed());
13870                         // User Validation Salt
13871                         $this->encryptdata['UVS'] = substr($seed, 0, 8);
13872                         // User Key Salt
13873                         $this->encryptdata['UKS'] = substr($seed, 8, 16);
13874                         return hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UVS'], true).$this->encryptdata['UVS'].$this->encryptdata['UKS'];
13875                 }
13876         }
13877 
13885         protected function _UEvalue() {
13886                 $hashkey = hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UKS'], true);
13887                 $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC));
13888                 return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $hashkey, $this->encryptdata['key'], MCRYPT_MODE_CBC, $iv);
13889         }
13890 
13898         protected function _Ovalue() {
13899                 if ($this->encryptdata['mode'] < 3) { // RC4-40, RC4-128, AES-128
13900                         $tmp = $this->_md5_16($this->encryptdata['owner_password']);
13901                         if ($this->encryptdata['mode'] > 0) {
13902                                 for ($i = 0; $i < 50; ++$i) {
13903                                         $tmp = $this->_md5_16($tmp);
13904                                 }
13905                         }
13906                         $owner_key = substr($tmp, 0, ($this->encryptdata['Length'] / 8));
13907                         $enc = $this->_RC4($owner_key, $this->encryptdata['user_password']);
13908                         if ($this->encryptdata['mode'] > 0) {
13909                                 $len = strlen($owner_key);
13910                                 for ($i = 1; $i <= 19; ++$i) {
13911                                         $ek = '';
13912                                         for ($j = 0; $j < $len; ++$j) {
13913                                                 $ek .= chr(ord($owner_key{$j}) ^ $i);
13914                                         }
13915                                         $enc = $this->_RC4($ek, $enc);
13916                                 }
13917                         }
13918                         return $enc;
13919                 } elseif ($this->encryptdata['mode'] == 3) { // AES-256
13920                         $seed = $this->_md5_16($this->getRandomSeed());
13921                         // Owner Validation Salt
13922                         $this->encryptdata['OVS'] = substr($seed, 0, 8);
13923                         // Owner Key Salt
13924                         $this->encryptdata['OKS'] = substr($seed, 8, 16);
13925                         return hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OVS'].$this->encryptdata['U'], true).$this->encryptdata['OVS'].$this->encryptdata['OKS'];
13926                 }
13927         }
13928 
13936         protected function _OEvalue() {
13937                 $hashkey = hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OKS'].$this->encryptdata['U'], true);
13938                 $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC));
13939                 return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $hashkey, $this->encryptdata['key'], MCRYPT_MODE_CBC, $iv);
13940         }
13941 
13950         protected function _fixAES256Password($password) {
13951                 $psw = ''; // password to be returned
13952                 $psw_array = $this->utf8Bidi($this->UTF8StringToArray($password), $password, $this->rtl);
13953                 foreach ($psw_array as $c) {
13954                         $psw .= $this->unichr($c);
13955                 }
13956                 return substr($psw, 0, 127);
13957         }
13958 
13965         protected function _generateencryptionkey() {
13966                 $keybytelen = ($this->encryptdata['Length'] / 8);
13967                 if (!$this->encryptdata['pubkey']) { // standard mode
13968                         if ($this->encryptdata['mode'] == 3) { // AES-256
13969                                 // generate 256 bit random key
13970                                 $this->encryptdata['key'] = substr(hash('sha256', $this->getRandomSeed(), true), 0, $keybytelen);
13971                                 // truncate passwords
13972                                 $this->encryptdata['user_password'] = $this->_fixAES256Password($this->encryptdata['user_password']);
13973                                 $this->encryptdata['owner_password'] = $this->_fixAES256Password($this->encryptdata['owner_password']);
13974                                 // Compute U value
13975                                 $this->encryptdata['U'] = $this->_Uvalue();
13976                                 // Compute UE value
13977                                 $this->encryptdata['UE'] = $this->_UEvalue();
13978                                 // Compute O value
13979                                 $this->encryptdata['O'] = $this->_Ovalue();
13980                                 // Compute OE value
13981                                 $this->encryptdata['OE'] = $this->_OEvalue();
13982                                 // Compute P value
13983                                 $this->encryptdata['P'] = $this->encryptdata['protection'];
13984                                 // Computing the encryption dictionary's Perms (permissions) value
13985                                 $perms = $this->getEncPermissionsString($this->encryptdata['protection']); // bytes 0-3
13986                                 $perms .= chr(255).chr(255).chr(255).chr(255); // bytes 4-7
13987                                 if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) { // byte 8
13988                                         $perms .= 'F';
13989                                 } else {
13990                                         $perms .= 'T';
13991                                 }
13992                                 $perms .= 'adb'; // bytes 9-11
13993                                 $perms .= 'nick'; // bytes 12-15
13994                                 $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB));
13995                                 $this->encryptdata['perms'] = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $this->encryptdata['key'], $perms, MCRYPT_MODE_ECB, $iv);
13996                         } else { // RC4-40, RC4-128, AES-128
13997                                 // Pad passwords
13998                                 $this->encryptdata['user_password'] = substr($this->encryptdata['user_password'].$this->enc_padding, 0, 32);
13999                                 $this->encryptdata['owner_password'] = substr($this->encryptdata['owner_password'].$this->enc_padding, 0, 32);
14000                                 // Compute O value
14001                                 $this->encryptdata['O'] = $this->_Ovalue();
14002                                 // get default permissions (reverse byte order)
14003                                 $permissions = $this->getEncPermissionsString($this->encryptdata['protection']);
14004                                 // Compute encryption key
14005                                 $tmp = $this->_md5_16($this->encryptdata['user_password'].$this->encryptdata['O'].$permissions.$this->encryptdata['fileid']);
14006                                 if ($this->encryptdata['mode'] > 0) {
14007                                         for ($i = 0; $i < 50; ++$i) {
14008                                                 $tmp = $this->_md5_16(substr($tmp, 0, $keybytelen));
14009                                         }
14010                                 }
14011                                 $this->encryptdata['key'] = substr($tmp, 0, $keybytelen);
14012                                 // Compute U value
14013                                 $this->encryptdata['U'] = $this->_Uvalue();
14014                                 // Compute P value
14015                                 $this->encryptdata['P'] = $this->encryptdata['protection'];
14016                         }
14017                 } else { // Public-Key mode
14018                         // random 20-byte seed
14019                         $seed = sha1($this->getRandomSeed(), true);
14020                         $recipient_bytes = '';
14021                         foreach ($this->encryptdata['pubkeys'] as $pubkey) {
14022                                 // for each public certificate
14023                                 if (isset($pubkey['p'])) {
14024                                         $pkprotection = $this->getUserPermissionCode($pubkey['p'], $this->encryptdata['mode']);
14025                                 } else {
14026                                         $pkprotection = $this->encryptdata['protection'];
14027                                 }
14028                                 // get default permissions (reverse byte order)
14029                                 $pkpermissions = $this->getEncPermissionsString($pkprotection);
14030                                 // envelope data
14031                                 $envelope = $seed.$pkpermissions;
14032                                 // write the envelope data to a temporary file
14033                                 $tempkeyfile = tempnam(K_PATH_CACHE, 'tmpkey_');
14034                                 $f = fopen($tempkeyfile, 'wb');
14035                                 if (!$f) {
14036                                         $this->Error('Unable to create temporary key file: '.$tempkeyfile);
14037                                 }
14038                                 $envelope_length = strlen($envelope);
14039                                 fwrite($f, $envelope, $envelope_length);
14040                                 fclose($f);
14041                                 $tempencfile = tempnam(K_PATH_CACHE, 'tmpenc_');
14042                                 if (!openssl_pkcs7_encrypt($tempkeyfile, $tempencfile, $pubkey['c'], array(), PKCS7_DETACHED | PKCS7_BINARY)) {
14043                                         $this->Error('Unable to encrypt the file: '.$tempkeyfile);
14044                                 }
14045                                 unlink($tempkeyfile);
14046                                 // read encryption signature
14047                                 $signature = file_get_contents($tempencfile, false, null, $envelope_length);
14048                                 unlink($tempencfile);
14049                                 // extract signature
14050                                 $signature = substr($signature, strpos($signature, 'Content-Disposition'));
14051                                 $tmparr = explode("\n\n", $signature);
14052                                 $signature = trim($tmparr[1]);
14053                                 unset($tmparr);
14054                                 // decode signature
14055                                 $signature = base64_decode($signature);
14056                                 // convert signature to hex
14057                                 $hexsignature = current(unpack('H*', $signature));
14058                                 // store signature on recipients array
14059                                 $this->encryptdata['Recipients'][] = $hexsignature;
14060                                 // The bytes of each item in the Recipients array of PKCS#7 objects in the order in which they appear in the array
14061                                 $recipient_bytes .= $signature;
14062                         }
14063                         // calculate encryption key
14064                         if ($this->encryptdata['mode'] == 3) { // AES-256
14065                                 $this->encryptdata['key'] = substr(hash('sha256', $seed.$recipient_bytes, true), 0, $keybytelen);
14066                         } else { // RC4-40, RC4-128, AES-128
14067                                 $this->encryptdata['key'] = substr(sha1($seed.$recipient_bytes, true), 0, $keybytelen);
14068                         }
14069                 }
14070         }
14071 
14080         protected function getUserPermissionCode($permissions, $mode=0) {
14081                 $options = array(
14082                         'owner' => 2, // bit 2 -- inverted logic: cleared by default
14083                         'print' => 4, // bit 3
14084                         'modify' => 8, // bit 4
14085                         'copy' => 16, // bit 5
14086                         'annot-forms' => 32, // bit 6
14087                         'fill-forms' => 256, // bit 9
14088                         'extract' => 512, // bit 10
14089                         'assemble' => 1024,// bit 11
14090                         'print-high' => 2048 // bit 12
14091                         );
14092                 $protection = 2147422012; // 32 bit: (01111111 11111111 00001111 00111100)
14093                 foreach ($permissions as $permission) {
14094                         if (!isset($options[$permission])) {
14095                                 $this->Error('Incorrect permission: '.$permission);
14096                         }
14097                         if (($mode > 0) OR ($options[$permission] <= 32)) {
14098                                 // set only valid permissions
14099                                 if ($options[$permission] == 2) {
14100                                         // the logic for bit 2 is inverted (cleared by default)
14101                                         $protection += $options[$permission];
14102                                 } else {
14103                                         $protection -= $options[$permission];
14104                                 }
14105                         }
14106                 }
14107                 return $protection;
14108         }
14109 
14124         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) {
14125                 if ($this->pdfa_mode) {
14126                         // encryption is not allowed in PDF/A mode
14127                         return;
14128                 }
14129                 $this->encryptdata['protection'] = $this->getUserPermissionCode($permissions, $mode);
14130                 if (($pubkeys !== null) AND (is_array($pubkeys))) {
14131                         // public-key mode
14132                         $this->encryptdata['pubkeys'] = $pubkeys;
14133                         if ($mode == 0) {
14134                                 // public-Key Security requires at least 128 bit
14135                                 $mode = 1;
14136                         }
14137                         if (!function_exists('openssl_pkcs7_encrypt')) {
14138                                 $this->Error('Public-Key Security requires openssl library.');
14139                         }
14140                         // Set Public-Key filter (availabe are: Entrust.PPKEF, Adobe.PPKLite, Adobe.PubSec)
14141                         $this->encryptdata['pubkey'] = true;
14142                         $this->encryptdata['Filter'] = 'Adobe.PubSec';
14143                         $this->encryptdata['StmF'] = 'DefaultCryptFilter';
14144                         $this->encryptdata['StrF'] = 'DefaultCryptFilter';
14145                 } else {
14146                         // standard mode (password mode)
14147                         $this->encryptdata['pubkey'] = false;
14148                         $this->encryptdata['Filter'] = 'Standard';
14149                         $this->encryptdata['StmF'] = 'StdCF';
14150                         $this->encryptdata['StrF'] = 'StdCF';
14151                 }
14152                 if ($mode > 1) { // AES
14153                         if (!extension_loaded('mcrypt')) {
14154                                 $this->Error('AES encryption requires mcrypt library (http://www.php.net/manual/en/mcrypt.requirements.php).');
14155                         }
14156                         if (mcrypt_get_cipher_name(MCRYPT_RIJNDAEL_128) === false) {
14157                                 $this->Error('AES encryption requires MCRYPT_RIJNDAEL_128 cypher.');
14158                         }
14159                         if (($mode == 3) AND !function_exists('hash')) {
14160                                 // the Hash extension requires no external libraries and is enabled by default as of PHP 5.1.2.
14161                                 $this->Error('AES 256 encryption requires HASH Message Digest Framework (http://www.php.net/manual/en/book.hash.php).');
14162                         }
14163                 }
14164                 if ($owner_pass === null) {
14165                         $owner_pass = md5($this->getRandomSeed());
14166                 }
14167                 $this->encryptdata['user_password'] = $user_pass;
14168                 $this->encryptdata['owner_password'] = $owner_pass;
14169                 $this->encryptdata['mode'] = $mode;
14170                 switch ($mode) {
14171                         case 0: { // RC4 40 bit
14172                                 $this->encryptdata['V'] = 1;
14173                                 $this->encryptdata['Length'] = 40;
14174                                 $this->encryptdata['CF']['CFM'] = 'V2';
14175                                 break;
14176                         }
14177                         case 1: { // RC4 128 bit
14178                                 $this->encryptdata['V'] = 2;
14179                                 $this->encryptdata['Length'] = 128;
14180                                 $this->encryptdata['CF']['CFM'] = 'V2';
14181                                 if ($this->encryptdata['pubkey']) {
14182                                         $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s4';
14183                                         $this->encryptdata['Recipients'] = array();
14184                                 }
14185                                 break;
14186                         }
14187                         case 2: { // AES 128 bit
14188                                 $this->encryptdata['V'] = 4;
14189                                 $this->encryptdata['Length'] = 128;
14190                                 $this->encryptdata['CF']['CFM'] = 'AESV2';
14191                                 $this->encryptdata['CF']['Length'] = 128;
14192                                 if ($this->encryptdata['pubkey']) {
14193                                         $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5';
14194                                         $this->encryptdata['Recipients'] = array();
14195                                 }
14196                                 break;
14197                         }
14198                         case 3: { // AES 256 bit
14199                                 $this->encryptdata['V'] = 5;
14200                                 $this->encryptdata['Length'] = 256;
14201                                 $this->encryptdata['CF']['CFM'] = 'AESV3';
14202                                 $this->encryptdata['CF']['Length'] = 256;
14203                                 if ($this->encryptdata['pubkey']) {
14204                                         $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5';
14205                                         $this->encryptdata['Recipients'] = array();
14206                                 }
14207                                 break;
14208                         }
14209                 }
14210                 $this->encrypted = true;
14211                 $this->encryptdata['fileid'] = $this->convertHexStringToString($this->file_id);
14212                 $this->_generateencryptionkey();
14213         }
14214 
14223         protected function convertHexStringToString($bs) {
14224                 $string = ''; // string to be returned
14225                 $bslength = strlen($bs);
14226                 if (($bslength % 2) != 0) {
14227                         // padding
14228                         $bs .= '0';
14229                         ++$bslength;
14230                 }
14231                 for ($i = 0; $i < $bslength; $i += 2) {
14232                         $string .= chr(hexdec($bs{$i}.$bs{($i + 1)}));
14233                 }
14234                 return $string;
14235         }
14236 
14245         protected function convertStringToHexString($s) {
14246                 $bs = '';
14247                 $chars = preg_split('//', $s, -1, PREG_SPLIT_NO_EMPTY);
14248                 foreach ($chars as $c) {
14249                         $bs .= sprintf('%02s', dechex(ord($c)));
14250                 }
14251                 return $bs;
14252         }
14253 
14262         protected function getEncPermissionsString($protection) {
14263                 $binprot = sprintf('%032b', $protection);
14264                 $str = chr(bindec(substr($binprot, 24, 8)));
14265                 $str .= chr(bindec(substr($binprot, 16, 8)));
14266                 $str .= chr(bindec(substr($binprot, 8, 8)));
14267                 $str .= chr(bindec(substr($binprot, 0, 8)));
14268                 return $str;
14269         }
14270 
14271         // END OF ENCRYPTION FUNCTIONS -------------------------
14272 
14273         // START TRANSFORMATIONS SECTION -----------------------
14274 
14283         public function StartTransform() {
14284                 $this->_out('q');
14285                 if ($this->inxobj) {
14286                         // we are inside an XObject template
14287                         $this->xobjects[$this->xobjid]['transfmrk'][] = strlen($this->xobjects[$this->xobjid]['outdata']);
14288                 } else {
14289                         $this->transfmrk[$this->page][] = $this->pagelen[$this->page];
14290                 }
14291                 ++$this->transfmatrix_key;
14292                 $this->transfmatrix[$this->transfmatrix_key] = array();
14293         }
14294 
14303         public function StopTransform() {
14304                 $this->_out('Q');
14305                 if (isset($this->transfmatrix[$this->transfmatrix_key])) {
14306                         array_pop($this->transfmatrix[$this->transfmatrix_key]);
14307                         --$this->transfmatrix_key;
14308                 }
14309                 if ($this->inxobj) {
14310                         // we are inside an XObject template
14311                         array_pop($this->xobjects[$this->xobjid]['transfmrk']);
14312                 } else {
14313                         array_pop($this->transfmrk[$this->page]);
14314                 }
14315         }
14325         public function ScaleX($s_x, $x='', $y='') {
14326                 $this->Scale($s_x, 100, $x, $y);
14327         }
14328 
14338         public function ScaleY($s_y, $x='', $y='') {
14339                 $this->Scale(100, $s_y, $x, $y);
14340         }
14341 
14351         public function ScaleXY($s, $x='', $y='') {
14352                 $this->Scale($s, $s, $x, $y);
14353         }
14354 
14365         public function Scale($s_x, $s_y, $x='', $y='') {
14366                 if ($x === '') {
14367                         $x = $this->x;
14368                 }
14369                 if ($y === '') {
14370                         $y = $this->y;
14371                 }
14372                 if (($s_x == 0) OR ($s_y == 0)) {
14373                         $this->Error('Please do not use values equal to zero for scaling');
14374                 }
14375                 $y = ($this->h - $y) * $this->k;
14376                 $x *= $this->k;
14377                 //calculate elements of transformation matrix
14378                 $s_x /= 100;
14379                 $s_y /= 100;
14380                 $tm = array();
14381                 $tm[0] = $s_x;
14382                 $tm[1] = 0;
14383                 $tm[2] = 0;
14384                 $tm[3] = $s_y;
14385                 $tm[4] = $x * (1 - $s_x);
14386                 $tm[5] = $y * (1 - $s_y);
14387                 //scale the coordinate system
14388                 $this->Transform($tm);
14389         }
14390 
14398         public function MirrorH($x='') {
14399                 $this->Scale(-100, 100, $x);
14400         }
14401 
14409         public function MirrorV($y='') {
14410                 $this->Scale(100, -100, '', $y);
14411         }
14412 
14421         public function MirrorP($x='',$y='') {
14422                 $this->Scale(-100, -100, $x, $y);
14423         }
14424 
14434         public function MirrorL($angle=0, $x='',$y='') {
14435                 $this->Scale(-100, 100, $x, $y);
14436                 $this->Rotate(-2*($angle-90), $x, $y);
14437         }
14438 
14446         public function TranslateX($t_x) {
14447                 $this->Translate($t_x, 0);
14448         }
14449 
14457         public function TranslateY($t_y) {
14458                 $this->Translate(0, $t_y);
14459         }
14460 
14469         public function Translate($t_x, $t_y) {
14470                 //calculate elements of transformation matrix
14471                 $tm = array();
14472                 $tm[0] = 1;
14473                 $tm[1] = 0;
14474                 $tm[2] = 0;
14475                 $tm[3] = 1;
14476                 $tm[4] = $t_x * $this->k;
14477                 $tm[5] = -$t_y * $this->k;
14478                 //translate the coordinate system
14479                 $this->Transform($tm);
14480         }
14481 
14491         public function Rotate($angle, $x='', $y='') {
14492                 if ($x === '') {
14493                         $x = $this->x;
14494                 }
14495                 if ($y === '') {
14496                         $y = $this->y;
14497                 }
14498                 $y = ($this->h - $y) * $this->k;
14499                 $x *= $this->k;
14500                 //calculate elements of transformation matrix
14501                 $tm = array();
14502                 $tm[0] = cos(deg2rad($angle));
14503                 $tm[1] = sin(deg2rad($angle));
14504                 $tm[2] = -$tm[1];
14505                 $tm[3] = $tm[0];
14506                 $tm[4] = $x + ($tm[1] * $y) - ($tm[0] * $x);
14507                 $tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x);
14508                 //rotate the coordinate system around ($x,$y)
14509                 $this->Transform($tm);
14510         }
14511 
14521         public function SkewX($angle_x, $x='', $y='') {
14522                 $this->Skew($angle_x, 0, $x, $y);
14523         }
14524 
14534         public function SkewY($angle_y, $x='', $y='') {
14535                 $this->Skew(0, $angle_y, $x, $y);
14536         }
14537 
14548         public function Skew($angle_x, $angle_y, $x='', $y='') {
14549                 if ($x === '') {
14550                         $x = $this->x;
14551                 }
14552                 if ($y === '') {
14553                         $y = $this->y;
14554                 }
14555                 if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) {
14556                         $this->Error('Please use values between -90 and +90 degrees for Skewing.');
14557                 }
14558                 $x *= $this->k;
14559                 $y = ($this->h - $y) * $this->k;
14560                 //calculate elements of transformation matrix
14561                 $tm = array();
14562                 $tm[0] = 1;
14563                 $tm[1] = tan(deg2rad($angle_y));
14564                 $tm[2] = tan(deg2rad($angle_x));
14565                 $tm[3] = 1;
14566                 $tm[4] = -$tm[2] * $y;
14567                 $tm[5] = -$tm[1] * $x;
14568                 //skew the coordinate system
14569                 $this->Transform($tm);
14570         }
14571 
14579         protected function Transform($tm) {
14580                 $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
14581                 // add tranformation matrix
14582                 $this->transfmatrix[$this->transfmatrix_key][] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]);
14583                 // update transformation mark
14584                 if ($this->inxobj) {
14585                         // we are inside an XObject template
14586                         if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
14587                                 $key = key($this->xobjects[$this->xobjid]['transfmrk']);
14588                                 $this->xobjects[$this->xobjid]['transfmrk'][$key] = strlen($this->xobjects[$this->xobjid]['outdata']);
14589                         }
14590                 } elseif (end($this->transfmrk[$this->page]) !== false) {
14591                         $key = key($this->transfmrk[$this->page]);
14592                         $this->transfmrk[$this->page][$key] = $this->pagelen[$this->page];
14593                 }
14594         }
14595 
14596         // END TRANSFORMATIONS SECTION -------------------------
14597 
14598         // START GRAPHIC FUNCTIONS SECTION ---------------------
14599         // The following section is based on the code provided by David Hernandez Sanz
14600 
14608         public function SetLineWidth($width) {
14609                 //Set line width
14610                 $this->LineWidth = $width;
14611                 $this->linestyleWidth = sprintf('%.2F w', ($width * $this->k));
14612                 if ($this->page > 0) {
14613                         $this->_out($this->linestyleWidth);
14614                 }
14615         }
14616 
14624         public function GetLineWidth() {
14625                 return $this->LineWidth;
14626         }
14627 
14651         public function SetLineStyle($style, $ret=false) {
14652                 $s = ''; // string to be returned
14653                 if (!is_array($style)) {
14654                         return;
14655                 }
14656                 if (isset($style['width'])) {
14657                         $this->LineWidth = $style['width'];
14658                         $this->linestyleWidth = sprintf('%.2F w', ($style['width'] * $this->k));
14659                         $s .= $this->linestyleWidth.' ';
14660                 }
14661                 if (isset($style['cap'])) {
14662                         $ca = array('butt' => 0, 'round'=> 1, 'square' => 2);
14663                         if (isset($ca[$style['cap']])) {
14664                                 $this->linestyleCap = $ca[$style['cap']].' J';
14665                                 $s .= $this->linestyleCap.' ';
14666                         }
14667                 }
14668                 if (isset($style['join'])) {
14669                         $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
14670                         if (isset($ja[$style['join']])) {
14671                                 $this->linestyleJoin = $ja[$style['join']].' j';
14672                                 $s .= $this->linestyleJoin.' ';
14673                         }
14674                 }
14675                 if (isset($style['dash'])) {
14676                         $dash_string = '';
14677                         if ($style['dash']) {
14678                                 if (preg_match('/^.+,/', $style['dash']) > 0) {
14679                                         $tab = explode(',', $style['dash']);
14680                                 } else {
14681                                         $tab = array($style['dash']);
14682                                 }
14683                                 $dash_string = '';
14684                                 foreach ($tab as $i => $v) {
14685                                         if ($i) {
14686                                                 $dash_string .= ' ';
14687                                         }
14688                                         $dash_string .= sprintf('%.2F', $v);
14689                                 }
14690                         }
14691                         if (!isset($style['phase']) OR !$style['dash']) {
14692                                 $style['phase'] = 0;
14693                         }
14694                         $this->linestyleDash = sprintf('[%s] %.2F d', $dash_string, $style['phase']);
14695                         $s .= $this->linestyleDash.' ';
14696                 }
14697                 if (isset($style['color'])) {
14698                         $s .= $this->SetDrawColorArray($style['color'], true).' ';
14699                 }
14700                 if (!$ret) {
14701                         $this->_out($s);
14702                 }
14703                 return $s;
14704         }
14705 
14713         protected function _outPoint($x, $y) {
14714                 $this->_out(sprintf('%.2F %.2F m', $x * $this->k, ($this->h - $y) * $this->k));
14715         }
14716 
14725         protected function _outLine($x, $y) {
14726                 $this->_out(sprintf('%.2F %.2F l', $x * $this->k, ($this->h - $y) * $this->k));
14727         }
14728 
14739         protected function _outRect($x, $y, $w, $h, $op) {
14740                 $this->_out(sprintf('%.2F %.2F %.2F %.2F re %s', $x * $this->k, ($this->h - $y) * $this->k, $w * $this->k, -$h * $this->k, $op));
14741         }
14742 
14755         protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) {
14756                 $this->_out(sprintf('%.2F %.2F %.2F %.2F %.2F %.2F 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));
14757         }
14758 
14769         protected function _outCurveV($x2, $y2, $x3, $y3) {
14770                 $this->_out(sprintf('%.2F %.2F %.2F %.2F v', $x2 * $this->k, ($this->h - $y2) * $this->k, $x3 * $this->k, ($this->h - $y3) * $this->k));
14771         }
14772 
14783         protected function _outCurveY($x1, $y1, $x3, $y3) {
14784                 $this->_out(sprintf('%.2F %.2F %.2F %.2F y', $x1 * $this->k, ($this->h - $y1) * $this->k, $x3 * $this->k, ($this->h - $y3) * $this->k));
14785         }
14786 
14798         public function Line($x1, $y1, $x2, $y2, $style=array()) {
14799                 if (is_array($style)) {
14800                         $this->SetLineStyle($style);
14801                 }
14802                 $this->_outPoint($x1, $y1);
14803                 $this->_outLine($x2, $y2);
14804                 $this->_out('S');
14805         }
14806 
14826         public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) {
14827                 if (!(false === strpos($style, 'F')) AND !empty($fill_color)) {
14828                         $this->SetFillColorArray($fill_color);
14829                 }
14830                 $op = $this->getPathPaintOperator($style);
14831                 if ((!$border_style) OR (isset($border_style['all']))) {
14832                         if (isset($border_style['all']) AND $border_style['all']) {
14833                                 $this->SetLineStyle($border_style['all']);
14834                                 $border_style = array();
14835                         }
14836                 }
14837                 $this->_outRect($x, $y, $w, $h, $op);
14838                 if ($border_style) {
14839                         $border_style2 = array();
14840                         foreach ($border_style as $line => $value) {
14841                                 $length = strlen($line);
14842                                 for ($i = 0; $i < $length; ++$i) {
14843                                         $border_style2[$line[$i]] = $value;
14844                                 }
14845                         }
14846                         $border_style = $border_style2;
14847                         if (isset($border_style['L']) AND $border_style['L']) {
14848                                 $this->Line($x, $y, $x, $y + $h, $border_style['L']);
14849                         }
14850                         if (isset($border_style['T']) AND $border_style['T']) {
14851                                 $this->Line($x, $y, $x + $w, $y, $border_style['T']);
14852                         }
14853                         if (isset($border_style['R']) AND $border_style['R']) {
14854                                 $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style['R']);
14855                         }
14856                         if (isset($border_style['B']) AND $border_style['B']) {
14857                                 $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style['B']);
14858                         }
14859                 }
14860         }
14861 
14881         public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) {
14882                 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
14883                         $this->SetFillColorArray($fill_color);
14884                 }
14885                 $op = $this->getPathPaintOperator($style);
14886                 if ($line_style) {
14887                         $this->SetLineStyle($line_style);
14888                 }
14889                 $this->_outPoint($x0, $y0);
14890                 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
14891                 $this->_out($op);
14892         }
14893 
14908         public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) {
14909                 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
14910                         $this->SetFillColorArray($fill_color);
14911                 }
14912                 $op = $this->getPathPaintOperator($style);
14913                 if ($op == 'f') {
14914                         $line_style = array();
14915                 }
14916                 if ($line_style) {
14917                         $this->SetLineStyle($line_style);
14918                 }
14919                 $this->_outPoint($x0, $y0);
14920                 foreach ($segments as $segment) {
14921                         list($x1, $y1, $x2, $y2, $x3, $y3) = $segment;
14922                         $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
14923                 }
14924                 $this->_out($op);
14925         }
14926 
14945         public function Ellipse($x0, $y0, $rx, $ry='', $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
14946                 if ($this->empty_string($ry) OR ($ry == 0)) {
14947                         $ry = $rx;
14948                 }
14949                 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
14950                         $this->SetFillColorArray($fill_color);
14951                 }
14952                 $op = $this->getPathPaintOperator($style);
14953                 if ($op == 'f') {
14954                         $line_style = array();
14955                 }
14956                 if ($line_style) {
14957                         $this->SetLineStyle($line_style);
14958                 }
14959                 $this->_outellipticalarc($x0, $y0, $rx, $ry, $angle, $astart, $afinish, false, $nc, true, true, false);
14960                 $this->_out($op);
14961         }
14962 
14983         protected function _outellipticalarc($xc, $yc, $rx, $ry, $xang=0, $angs=0, $angf=360, $pie=false, $nc=2, $startpoint=true, $ccw=true, $svg=false) {
14984                 $k = $this->k;
14985                 if ($nc < 2) {
14986                         $nc = 2;
14987                 }
14988                 $xmin = 2147483647;
14989                 $ymin = 2147483647;
14990                 $xmax = 0;
14991                 $ymax = 0;
14992                 if ($pie) {
14993                         // center of the arc
14994                         $this->_outPoint($xc, $yc);
14995                 }
14996                 $xang = deg2rad((float) $xang);
14997                 $angs = deg2rad((float) $angs);
14998                 $angf = deg2rad((float) $angf);
14999                 if ($svg) {
15000                         $as = $angs;
15001                         $af = $angf;
15002                 } else {
15003                         $as = atan2((sin($angs) / $ry), (cos($angs) / $rx));
15004                         $af = atan2((sin($angf) / $ry), (cos($angf) / $rx));
15005                 }
15006                 if ($as < 0) {
15007                         $as += (2 * M_PI);
15008                 }
15009                 if ($af < 0) {
15010                         $af += (2 * M_PI);
15011                 }
15012                 if ($ccw AND ($as > $af)) {
15013                         // reverse rotation
15014                         $as -= (2 * M_PI);
15015                 } elseif (!$ccw AND ($as < $af)) {
15016                         // reverse rotation
15017                         $af -= (2 * M_PI);
15018                 }
15019                 $total_angle = ($af - $as);
15020                 if ($nc < 2) {
15021                         $nc = 2;
15022                 }
15023                 // total arcs to draw
15024                 $nc *= (2 * abs($total_angle) / M_PI);
15025                 $nc = round($nc) + 1;
15026                 // angle of each arc
15027                 $arcang = ($total_angle / $nc);
15028                 // center point in PDF coordinates
15029                 $x0 = $xc;
15030                 $y0 = ($this->h - $yc);
15031                 // starting angle
15032                 $ang = $as;
15033                 $alpha = sin($arcang) * ((sqrt(4 + (3 * pow(tan(($arcang) / 2), 2))) - 1) / 3);
15034                 $cos_xang = cos($xang);
15035                 $sin_xang = sin($xang);
15036                 $cos_ang = cos($ang);
15037                 $sin_ang = sin($ang);
15038                 // first arc point
15039                 $px1 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
15040                 $py1 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang);
15041                 // first Bezier control point
15042                 $qx1 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
15043                 $qy1 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang)));
15044                 if ($pie) {
15045                         // line from center to arc starting point
15046                         $this->_outLine($px1, $this->h - $py1);
15047                 } elseif ($startpoint) {
15048                         // arc starting point
15049                         $this->_outPoint($px1, $this->h - $py1);
15050                 }
15051                 // draw arcs
15052                 for ($i = 1; $i <= $nc; ++$i) {
15053                         // starting angle
15054                         $ang = $as + ($i * $arcang);
15055                         if ($i == $nc) {
15056                                 $ang = $af;
15057                         }
15058                         $cos_ang = cos($ang);
15059                         $sin_ang = sin($ang);
15060                         // second arc point
15061                         $px2 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
15062                         $py2 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang);
15063                         // second Bezier control point
15064                         $qx2 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
15065                         $qy2 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang)));
15066                         // draw arc
15067                         $cx1 = ($px1 + $qx1);
15068                         $cy1 = ($this->h - ($py1 + $qy1));
15069                         $cx2 = ($px2 - $qx2);
15070                         $cy2 = ($this->h - ($py2 - $qy2));
15071                         $cx3 = $px2;
15072                         $cy3 = ($this->h - $py2);
15073                         $this->_outCurve($cx1, $cy1, $cx2, $cy2, $cx3, $cy3);
15074                         // get bounding box coordinates
15075                         $xmin = min($xmin, $cx1, $cx2, $cx3);
15076                         $ymin = min($ymin, $cy1, $cy2, $cy3);
15077                         $xmax = max($xmax, $cx1, $cx2, $cx3);
15078                         $ymax = max($ymax, $cy1, $cy2, $cy3);
15079                         // move to next point
15080                         $px1 = $px2;
15081                         $py1 = $py2;
15082                         $qx1 = $qx2;
15083                         $qy1 = $qy2;
15084                 }
15085                 if ($pie) {
15086                         $this->_outLine($xc, $yc);
15087                         // get bounding box coordinates
15088                         $xmin = min($xmin, $xc);
15089                         $ymin = min($ymin, $yc);
15090                         $xmax = max($xmax, $xc);
15091                         $ymax = max($ymax, $yc);
15092                 }
15093                 return array($xmin, $ymin, $xmax, $ymax);
15094         }
15095 
15111         public function Circle($x0, $y0, $r, $angstr=0, $angend=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
15112                 $this->Ellipse($x0, $y0, $r, $r, 0, $angstr, $angend, $style, $line_style, $fill_color, $nc);
15113         }
15114 
15129         public function PolyLine($p, $style='', $line_style=array(), $fill_color=array()) {
15130                 $this->Polygon($p, $style, $line_style, $fill_color, false);
15131         }
15132 
15148         public function Polygon($p, $style='', $line_style=array(), $fill_color=array(), $closed=true) {
15149                 $nc = count($p); // number of coordinates
15150                 $np = $nc / 2; // number of points
15151                 if ($closed) {
15152                         // close polygon by adding the first 2 points at the end (one line)
15153                         for ($i = 0; $i < 4; ++$i) {
15154                                 $p[$nc + $i] = $p[$i];
15155                         }
15156                         // copy style for the last added line
15157                         if (isset($line_style[0])) {
15158                                 $line_style[$np] = $line_style[0];
15159                         }
15160                         $nc += 4;
15161                 }
15162                 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
15163                         $this->SetFillColorArray($fill_color);
15164                 }
15165                 $op = $this->getPathPaintOperator($style);
15166                 if ($op == 'f') {
15167                         $line_style = array();
15168                 }
15169                 $draw = true;
15170                 if ($line_style) {
15171                         if (isset($line_style['all'])) {
15172                                 $this->SetLineStyle($line_style['all']);
15173                         } else {
15174                                 $draw = false;
15175                                 if ($op == 'B') {
15176                                         // draw fill
15177                                         $op = 'f';
15178                                         $this->_outPoint($p[0], $p[1]);
15179                                         for ($i = 2; $i < $nc; $i = $i + 2) {
15180                                                 $this->_outLine($p[$i], $p[$i + 1]);
15181                                         }
15182                                         $this->_out($op);
15183                                 }
15184                                 // draw outline
15185                                 $this->_outPoint($p[0], $p[1]);
15186                                 for ($i = 2; $i < $nc; $i = $i + 2) {
15187                                         $line_num = ($i / 2) - 1;
15188                                         if (isset($line_style[$line_num])) {
15189                                                 if ($line_style[$line_num] != 0) {
15190                                                         if (is_array($line_style[$line_num])) {
15191                                                                 $this->_out('S');
15192                                                                 $this->SetLineStyle($line_style[$line_num]);
15193                                                                 $this->_outPoint($p[$i - 2], $p[$i - 1]);
15194                                                                 $this->_outLine($p[$i], $p[$i + 1]);
15195                                                                 $this->_out('S');
15196                                                                 $this->_outPoint($p[$i], $p[$i + 1]);
15197                                                         } else {
15198                                                                 $this->_outLine($p[$i], $p[$i + 1]);
15199                                                         }
15200                                                 }
15201                                         } else {
15202                                                 $this->_outLine($p[$i], $p[$i + 1]);
15203                                         }
15204                                 }
15205                                 $this->_out($op);
15206                         }
15207                 }
15208                 if ($draw) {
15209                         $this->_outPoint($p[0], $p[1]);
15210                         for ($i = 2; $i < $nc; $i = $i + 2) {
15211                                 $this->_outLine($p[$i], $p[$i + 1]);
15212                         }
15213                         $this->_out($op);
15214                 }
15215         }
15216 
15246         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()) {
15247                 if (3 > $ns) {
15248                         $ns = 3;
15249                 }
15250                 if ($draw_circle) {
15251                         $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
15252                 }
15253                 $p = array();
15254                 for ($i = 0; $i < $ns; ++$i) {
15255                         $a = $angle + ($i * 360 / $ns);
15256                         $a_rad = deg2rad((float) $a);
15257                         $p[] = $x0 + ($r * sin($a_rad));
15258                         $p[] = $y0 + ($r * cos($a_rad));
15259                 }
15260                 $this->Polygon($p, $style, $line_style, $fill_color);
15261         }
15262 
15294         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()) {
15295                 if ($nv < 2) {
15296                         $nv = 2;
15297                 }
15298                 if ($draw_circle) {
15299                         $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
15300                 }
15301                 $p2 = array();
15302                 $visited = array();
15303                 for ($i = 0; $i < $nv; ++$i) {
15304                         $a = $angle + ($i * 360 / $nv);
15305                         $a_rad = deg2rad((float) $a);
15306                         $p2[] = $x0 + ($r * sin($a_rad));
15307                         $p2[] = $y0 + ($r * cos($a_rad));
15308                         $visited[] = false;
15309                 }
15310                 $p = array();
15311                 $i = 0;
15312                 do {
15313                         $p[] = $p2[$i * 2];
15314                         $p[] = $p2[($i * 2) + 1];
15315                         $visited[$i] = true;
15316                         $i += $ng;
15317                         $i %= $nv;
15318                 } while (!$visited[$i]);
15319                 $this->Polygon($p, $style, $line_style, $fill_color);
15320         }
15321 
15336         public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
15337                 $this->RoundedRectXY($x, $y, $w, $h, $r, $r, $round_corner, $style, $border_style, $fill_color);
15338         }
15339 
15355         public function RoundedRectXY($x, $y, $w, $h, $rx, $ry, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
15356                 if (($round_corner == '0000') OR (($rx == $ry) AND ($rx == 0))) {
15357                         // Not rounded
15358                         $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
15359                         return;
15360                 }
15361                 // Rounded
15362                 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
15363                         $this->SetFillColorArray($fill_color);
15364                 }
15365                 $op = $this->getPathPaintOperator($style);
15366                 if ($op == 'f') {
15367                         $border_style = array();
15368                 }
15369                 if ($border_style) {
15370                         $this->SetLineStyle($border_style);
15371                 }
15372                 $MyArc = 4 / 3 * (sqrt(2) - 1);
15373                 $this->_outPoint($x + $rx, $y);
15374                 $xc = $x + $w - $rx;
15375                 $yc = $y + $ry;
15376                 $this->_outLine($xc, $y);
15377                 if ($round_corner[0]) {
15378                         $this->_outCurve($xc + ($rx * $MyArc), $yc - $ry, $xc + $rx, $yc - ($ry * $MyArc), $xc + $rx, $yc);
15379                 } else {
15380                         $this->_outLine($x + $w, $y);
15381                 }
15382                 $xc = $x + $w - $rx;
15383                 $yc = $y + $h - $ry;
15384                 $this->_outLine($x + $w, $yc);
15385                 if ($round_corner[1]) {
15386                         $this->_outCurve($xc + $rx, $yc + ($ry * $MyArc), $xc + ($rx * $MyArc), $yc + $ry, $xc, $yc + $ry);
15387                 } else {
15388                         $this->_outLine($x + $w, $y + $h);
15389                 }
15390                 $xc = $x + $rx;
15391                 $yc = $y + $h - $ry;
15392                 $this->_outLine($xc, $y + $h);
15393                 if ($round_corner[2]) {
15394                         $this->_outCurve($xc - ($rx * $MyArc), $yc + $ry, $xc - $rx, $yc + ($ry * $MyArc), $xc - $rx, $yc);
15395                 } else {
15396                         $this->_outLine($x, $y + $h);
15397                 }
15398                 $xc = $x + $rx;
15399                 $yc = $y + $ry;
15400                 $this->_outLine($x, $yc);
15401                 if ($round_corner[3]) {
15402                         $this->_outCurve($xc - $rx, $yc - ($ry * $MyArc), $xc - ($rx * $MyArc), $yc - $ry, $xc, $yc - $ry);
15403                 } else {
15404                         $this->_outLine($x, $y);
15405                         $this->_outLine($x + $rx, $y);
15406                 }
15407                 $this->_out($op);
15408         }
15409 
15422         public function Arrow($x0, $y0, $x1, $y1, $head_style=0, $arm_size=5, $arm_angle=15) {
15423                 // getting arrow direction angle
15424                 // 0 deg angle is when both arms go along X axis. angle grows clockwise.
15425                 $dir_angle = atan2(($y0 - $y1), ($x0 - $x1));
15426                 if ($dir_angle < 0) {
15427                         $dir_angle += (2 * M_PI);
15428                 }
15429                 $arm_angle = deg2rad($arm_angle);
15430                 $sx1 = $x1;
15431                 $sy1 = $y1;
15432                 if ($head_style > 0) {
15433                         // calculate the stopping point for the arrow shaft
15434                         $sx1 = $x1 + (($arm_size - $this->LineWidth) * cos($dir_angle));
15435                         $sy1 = $y1 + (($arm_size - $this->LineWidth) * sin($dir_angle));
15436                 }
15437                 // main arrow line / shaft
15438                 $this->Line($x0, $y0, $sx1, $sy1);
15439                 // left arrowhead arm tip
15440                 $x2L = $x1 + ($arm_size * cos($dir_angle + $arm_angle));
15441                 $y2L = $y1 + ($arm_size * sin($dir_angle + $arm_angle));
15442                 // right arrowhead arm tip
15443                 $x2R = $x1 + ($arm_size * cos($dir_angle - $arm_angle));
15444                 $y2R = $y1 + ($arm_size * sin($dir_angle - $arm_angle));
15445                 $mode = 'D';
15446                 $style = array();
15447                 switch ($head_style) {
15448                         case 0: {
15449                                 // draw only arrowhead arms
15450                                 $mode = 'D';
15451                                 $style = array(1, 1, 0);
15452                                 break;
15453                         }
15454                         case 1: {
15455                                 // draw closed arrowhead, but no fill
15456                                 $mode = 'D';
15457                                 break;
15458                         }
15459                         case 2: {
15460                                 // closed and filled arrowhead
15461                                 $mode = 'DF';
15462                                 break;
15463                         }
15464                         case 3: {
15465                                 // filled arrowhead
15466                                 $mode = 'F';
15467                                 break;
15468                         }
15469                 }
15470                 $this->Polygon(array($x2L, $y2L, $x1, $y1, $x2R, $y2R), $mode, $style, array());
15471         }
15472 
15473         // END GRAPHIC FUNCTIONS SECTION -----------------------
15474 
15475         // BIDIRECTIONAL TEXT SECTION --------------------------
15476 
15487         protected function utf8StrRev($str, $setbom=false, $forcertl=false) {
15488                 return $this->utf8StrArrRev($this->UTF8StringToArray($str), $str, $setbom, $forcertl);
15489         }
15490 
15502         protected function utf8StrArrRev($arr, $str='', $setbom=false, $forcertl=false) {
15503                 return $this->arrUTF8ToUTF16BE($this->utf8Bidi($arr, $str, $forcertl), $setbom);
15504         }
15505 
15516         protected function utf8Bidi($ta, $str='', $forcertl=false) {
15517                 // paragraph embedding level
15518                 $pel = 0;
15519                 // max level
15520                 $maxlevel = 0;
15521                 if ($this->empty_string($str)) {
15522                         // create string from array
15523                         $str = $this->UTF8ArrSubString($ta);
15524                 }
15525                 // check if string contains arabic text
15526                 if (preg_match($this->unicode->uni_RE_PATTERN_ARABIC, $str)) {
15527                         $arabic = true;
15528                 } else {
15529                         $arabic = false;
15530                 }
15531                 // check if string contains RTL text
15532                 if (!($forcertl OR $arabic OR preg_match($this->unicode->uni_RE_PATTERN_RTL, $str))) {
15533                         return $ta;
15534                 }
15535 
15536                 // get number of chars
15537                 $numchars = count($ta);
15538 
15539                 if ($forcertl == 'R') {
15540                         $pel = 1;
15541                 } elseif ($forcertl == 'L') {
15542                         $pel = 0;
15543                 } else {
15544                         // P2. In each paragraph, find the first character of type L, AL, or R.
15545                         // 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.
15546                         for ($i=0; $i < $numchars; ++$i) {
15547                                 $type = $this->unicode->uni_type[$ta[$i]];
15548                                 if ($type == 'L') {
15549                                         $pel = 0;
15550                                         break;
15551                                 } elseif (($type == 'AL') OR ($type == 'R')) {
15552                                         $pel = 1;
15553                                         break;
15554                                 }
15555                         }
15556                 }
15557 
15558                 // Current Embedding Level
15559                 $cel = $pel;
15560                 // directional override status
15561                 $dos = 'N';
15562                 $remember = array();
15563                 // start-of-level-run
15564                 $sor = $pel % 2 ? 'R' : 'L';
15565                 $eor = $sor;
15566 
15567                 // Array of characters data
15568                 $chardata = Array();
15569 
15570                 // 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.
15571                 // In the resolution of levels in rules I1 and I2, the maximum embedding level of 62 can be reached.
15572                 for ($i=0; $i < $numchars; ++$i) {
15573                         if ($ta[$i] == $this->unicode->uni_RLE) {
15574                                 // X2. With each RLE, compute the least greater odd embedding level.
15575                                 //      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.
15576                                 //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
15577                                 $next_level = $cel + ($cel % 2) + 1;
15578                                 if ($next_level < 62) {
15579                                         $remember[] = array('num' => $this->unicode->uni_RLE, 'cel' => $cel, 'dos' => $dos);
15580                                         $cel = $next_level;
15581                                         $dos = 'N';
15582                                         $sor = $eor;
15583                                         $eor = $cel % 2 ? 'R' : 'L';
15584                                 }
15585                         } elseif ($ta[$i] == $this->unicode->uni_LRE) {
15586                                 // X3. With each LRE, compute the least greater even embedding level.
15587                                 //      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.
15588                                 //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
15589                                 $next_level = $cel + 2 - ($cel % 2);
15590                                 if ( $next_level < 62 ) {
15591                                         $remember[] = array('num' => $this->unicode->uni_LRE, 'cel' => $cel, 'dos' => $dos);
15592                                         $cel = $next_level;
15593                                         $dos = 'N';
15594                                         $sor = $eor;
15595                                         $eor = $cel % 2 ? 'R' : 'L';
15596                                 }
15597                         } elseif ($ta[$i] == $this->unicode->uni_RLO) {
15598                                 // X4. With each RLO, compute the least greater odd embedding level.
15599                                 //      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.
15600                                 //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
15601                                 $next_level = $cel + ($cel % 2) + 1;
15602                                 if ($next_level < 62) {
15603                                         $remember[] = array('num' => $this->unicode->uni_RLO, 'cel' => $cel, 'dos' => $dos);
15604                                         $cel = $next_level;
15605                                         $dos = 'R';
15606                                         $sor = $eor;
15607                                         $eor = $cel % 2 ? 'R' : 'L';
15608                                 }
15609                         } elseif ($ta[$i] == $this->unicode->uni_LRO) {
15610                                 // X5. With each LRO, compute the least greater even embedding level.
15611                                 //      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.
15612                                 //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
15613                                 $next_level = $cel + 2 - ($cel % 2);
15614                                 if ( $next_level < 62 ) {
15615                                         $remember[] = array('num' => $this->unicode->uni_LRO, 'cel' => $cel, 'dos' => $dos);
15616                                         $cel = $next_level;
15617                                         $dos = 'L';
15618                                         $sor = $eor;
15619                                         $eor = $cel % 2 ? 'R' : 'L';
15620                                 }
15621                         } elseif ($ta[$i] == $this->unicode->uni_PDF) {
15622                                 // 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.
15623                                 if (count($remember)) {
15624                                         $last = count($remember ) - 1;
15625                                         if (($remember[$last]['num'] == $this->unicode->uni_RLE) OR
15626                                                 ($remember[$last]['num'] == $this->unicode->uni_LRE) OR
15627                                                 ($remember[$last]['num'] == $this->unicode->uni_RLO) OR
15628                                                 ($remember[$last]['num'] == $this->unicode->uni_LRO)) {
15629                                                 $match = array_pop($remember);
15630                                                 $cel = $match['cel'];
15631                                                 $dos = $match['dos'];
15632                                                 $sor = $eor;
15633                                                 $eor = ($cel > $match['cel'] ? $cel : $match['cel']) % 2 ? 'R' : 'L';
15634                                         }
15635                                 }
15636                         } elseif (($ta[$i] != $this->unicode->uni_RLE) AND
15637                                                          ($ta[$i] != $this->unicode->uni_LRE) AND
15638                                                          ($ta[$i] != $this->unicode->uni_RLO) AND
15639                                                          ($ta[$i] != $this->unicode->uni_LRO) AND
15640                                                          ($ta[$i] != $this->unicode->uni_PDF)) {
15641                                 // X6. For all types besides RLE, LRE, RLO, LRO, and PDF:
15642                                 //      a. Set the level of the current character to the current embedding level.
15643                                 //      b. Whenever the directional override status is not neutral, reset the current character type to the directional override status.
15644                                 if ($dos != 'N') {
15645                                         $chardir = $dos;
15646                                 } else {
15647                                         if (isset($this->unicode->uni_type[$ta[$i]])) {
15648                                                 $chardir = $this->unicode->uni_type[$ta[$i]];
15649                                         } else {
15650                                                 $chardir = 'L';
15651                                         }
15652                                 }
15653                                 // stores string characters and other information
15654                                 $chardata[] = array('char' => $ta[$i], 'level' => $cel, 'type' => $chardir, 'sor' => $sor, 'eor' => $eor);
15655                         }
15656                 } // end for each char
15657 
15658                 // X8. All explicit directional embeddings and overrides are completely terminated at the end of each paragraph. Paragraph separators are not included in the embedding.
15659                 // X9. Remove all RLE, LRE, RLO, LRO, PDF, and BN codes.
15660                 // 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.
15661 
15662                 // 3.3.3 Resolving Weak Types
15663                 // 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.
15664                 // Nonspacing marks are now resolved based on the previous characters.
15665                 $numchars = count($chardata);
15666 
15667                 // 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.
15668                 $prevlevel = -1; // track level changes
15669                 $levcount = 0; // counts consecutive chars at the same level
15670                 for ($i=0; $i < $numchars; ++$i) {
15671                         if ($chardata[$i]['type'] == 'NSM') {
15672                                 if ($levcount) {
15673                                         $chardata[$i]['type'] = $chardata[$i]['sor'];
15674                                 } elseif ($i > 0) {
15675                                         $chardata[$i]['type'] = $chardata[($i-1)]['type'];
15676                                 }
15677                         }
15678                         if ($chardata[$i]['level'] != $prevlevel) {
15679                                 $levcount = 0;
15680                         } else {
15681                                 ++$levcount;
15682                         }
15683                         $prevlevel = $chardata[$i]['level'];
15684                 }
15685 
15686                 // 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.
15687                 $prevlevel = -1;
15688                 $levcount = 0;
15689                 for ($i=0; $i < $numchars; ++$i) {
15690                         if ($chardata[$i]['char'] == 'EN') {
15691                                 for ($j=$levcount; $j >= 0; $j--) {
15692                                         if ($chardata[$j]['type'] == 'AL') {
15693                                                 $chardata[$i]['type'] = 'AN';
15694                                         } elseif (($chardata[$j]['type'] == 'L') OR ($chardata[$j]['type'] == 'R')) {
15695                                                 break;
15696                                         }
15697                                 }
15698                         }
15699                         if ($chardata[$i]['level'] != $prevlevel) {
15700                                 $levcount = 0;
15701                         } else {
15702                                 ++$levcount;
15703                         }
15704                         $prevlevel = $chardata[$i]['level'];
15705                 }
15706 
15707                 // W3. Change all ALs to R.
15708                 for ($i=0; $i < $numchars; ++$i) {
15709                         if ($chardata[$i]['type'] == 'AL') {
15710                                 $chardata[$i]['type'] = 'R';
15711                         }
15712                 }
15713 
15714                 // 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.
15715                 $prevlevel = -1;
15716                 $levcount = 0;
15717                 for ($i=0; $i < $numchars; ++$i) {
15718                         if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
15719                                 if (($chardata[$i]['type'] == 'ES') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
15720                                         $chardata[$i]['type'] = 'EN';
15721                                 } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
15722                                         $chardata[$i]['type'] = 'EN';
15723                                 } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'AN') AND ($chardata[($i+1)]['type'] == 'AN')) {
15724                                         $chardata[$i]['type'] = 'AN';
15725                                 }
15726                         }
15727                         if ($chardata[$i]['level'] != $prevlevel) {
15728                                 $levcount = 0;
15729                         } else {
15730                                 ++$levcount;
15731                         }
15732                         $prevlevel = $chardata[$i]['level'];
15733                 }
15734 
15735                 // W5. A sequence of European terminators adjacent to European numbers changes to all European numbers.
15736                 $prevlevel = -1;
15737                 $levcount = 0;
15738                 for ($i=0; $i < $numchars; ++$i) {
15739                         if ($chardata[$i]['type'] == 'ET') {
15740                                 if (($levcount > 0) AND ($chardata[($i-1)]['type'] == 'EN')) {
15741                                         $chardata[$i]['type'] = 'EN';
15742                                 } else {
15743                                         $j = $i+1;
15744                                         while (($j < $numchars) AND ($chardata[$j]['level'] == $prevlevel)) {
15745                                                 if ($chardata[$j]['type'] == 'EN') {
15746                                                         $chardata[$i]['type'] = 'EN';
15747                                                         break;
15748                                                 } elseif ($chardata[$j]['type'] != 'ET') {
15749                                                         break;
15750                                                 }
15751                                                 ++$j;
15752                                         }
15753                                 }
15754                         }
15755                         if ($chardata[$i]['level'] != $prevlevel) {
15756                                 $levcount = 0;
15757                         } else {
15758                                 ++$levcount;
15759                         }
15760                         $prevlevel = $chardata[$i]['level'];
15761                 }
15762 
15763                 // W6. Otherwise, separators and terminators change to Other Neutral.
15764                 $prevlevel = -1;
15765                 $levcount = 0;
15766                 for ($i=0; $i < $numchars; ++$i) {
15767                         if (($chardata[$i]['type'] == 'ET') OR ($chardata[$i]['type'] == 'ES') OR ($chardata[$i]['type'] == 'CS')) {
15768                                 $chardata[$i]['type'] = 'ON';
15769                         }
15770                         if ($chardata[$i]['level'] != $prevlevel) {
15771                                 $levcount = 0;
15772                         } else {
15773                                 ++$levcount;
15774                         }
15775                         $prevlevel = $chardata[$i]['level'];
15776                 }
15777 
15778                 //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.
15779                 $prevlevel = -1;
15780                 $levcount = 0;
15781                 for ($i=0; $i < $numchars; ++$i) {
15782                         if ($chardata[$i]['char'] == 'EN') {
15783                                 for ($j=$levcount; $j >= 0; $j--) {
15784                                         if ($chardata[$j]['type'] == 'L') {
15785                                                 $chardata[$i]['type'] = 'L';
15786                                         } elseif ($chardata[$j]['type'] == 'R') {
15787                                                 break;
15788                                         }
15789                                 }
15790                         }
15791                         if ($chardata[$i]['level'] != $prevlevel) {
15792                                 $levcount = 0;
15793                         } else {
15794                                 ++$levcount;
15795                         }
15796                         $prevlevel = $chardata[$i]['level'];
15797                 }
15798 
15799                 // 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.
15800                 $prevlevel = -1;
15801                 $levcount = 0;
15802                 for ($i=0; $i < $numchars; ++$i) {
15803                         if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
15804                                 if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
15805                                         $chardata[$i]['type'] = 'L';
15806                                 } elseif (($chardata[$i]['type'] == 'N') AND
15807                                  (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
15808                                  (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
15809                                         $chardata[$i]['type'] = 'R';
15810                                 } elseif ($chardata[$i]['type'] == 'N') {
15811                                         // N2. Any remaining neutrals take the embedding direction
15812                                         $chardata[$i]['type'] = $chardata[$i]['sor'];
15813                                 }
15814                         } elseif (($levcount == 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
15815                                 // first char
15816                                 if (($chardata[$i]['type'] == 'N') AND ($chardata[$i]['sor'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
15817                                         $chardata[$i]['type'] = 'L';
15818                                 } elseif (($chardata[$i]['type'] == 'N') AND
15819                                  (($chardata[$i]['sor'] == 'R') OR ($chardata[$i]['sor'] == 'EN') OR ($chardata[$i]['sor'] == 'AN')) AND
15820                                  (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
15821                                         $chardata[$i]['type'] = 'R';
15822                                 } elseif ($chardata[$i]['type'] == 'N') {
15823                                         // N2. Any remaining neutrals take the embedding direction
15824                                         $chardata[$i]['type'] = $chardata[$i]['sor'];
15825                                 }
15826                         } elseif (($levcount > 0) AND ((($i+1) == $numchars) OR (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] != $prevlevel))) {
15827                                 //last char
15828                                 if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[$i]['eor'] == 'L')) {
15829                                         $chardata[$i]['type'] = 'L';
15830                                 } elseif (($chardata[$i]['type'] == 'N') AND
15831                                  (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
15832                                  (($chardata[$i]['eor'] == 'R') OR ($chardata[$i]['eor'] == 'EN') OR ($chardata[$i]['eor'] == 'AN'))) {
15833                                         $chardata[$i]['type'] = 'R';
15834                                 } elseif ($chardata[$i]['type'] == 'N') {
15835                                         // N2. Any remaining neutrals take the embedding direction
15836                                         $chardata[$i]['type'] = $chardata[$i]['sor'];
15837                                 }
15838                         } elseif ($chardata[$i]['type'] == 'N') {
15839                                 // N2. Any remaining neutrals take the embedding direction
15840                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
15841                         }
15842                         if ($chardata[$i]['level'] != $prevlevel) {
15843                                 $levcount = 0;
15844                         } else {
15845                                 ++$levcount;
15846                         }
15847                         $prevlevel = $chardata[$i]['level'];
15848                 }
15849 
15850                 // 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.
15851                 // I2. For all characters with an odd (right-to-left) embedding direction, those of type L, EN or AN go up one level.
15852                 for ($i=0; $i < $numchars; ++$i) {
15853                         $odd = $chardata[$i]['level'] % 2;
15854                         if ($odd) {
15855                                 if (($chardata[$i]['type'] == 'L') OR ($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')) {
15856                                         $chardata[$i]['level'] += 1;
15857                                 }
15858                         } else {
15859                                 if ($chardata[$i]['type'] == 'R') {
15860                                         $chardata[$i]['level'] += 1;
15861                                 } elseif (($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')) {
15862                                         $chardata[$i]['level'] += 2;
15863                                 }
15864                         }
15865                         $maxlevel = max($chardata[$i]['level'],$maxlevel);
15866                 }
15867 
15868                 // L1. On each line, reset the embedding level of the following characters to the paragraph embedding level:
15869                 //      1. Segment separators,
15870                 //      2. Paragraph separators,
15871                 //      3. Any sequence of whitespace characters preceding a segment separator or paragraph separator, and
15872                 //      4. Any sequence of white space characters at the end of the line.
15873                 for ($i=0; $i < $numchars; ++$i) {
15874                         if (($chardata[$i]['type'] == 'B') OR ($chardata[$i]['type'] == 'S')) {
15875                                 $chardata[$i]['level'] = $pel;
15876                         } elseif ($chardata[$i]['type'] == 'WS') {
15877                                 $j = $i+1;
15878                                 while ($j < $numchars) {
15879                                         if ((($chardata[$j]['type'] == 'B') OR ($chardata[$j]['type'] == 'S')) OR
15880                                                 (($j == ($numchars-1)) AND ($chardata[$j]['type'] == 'WS'))) {
15881                                                 $chardata[$i]['level'] = $pel;
15882                                                 break;
15883                                         } elseif ($chardata[$j]['type'] != 'WS') {
15884                                                 break;
15885                                         }
15886                                         ++$j;
15887                                 }
15888                         }
15889                 }
15890 
15891                 // Arabic Shaping
15892                 // 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.
15893                 if ($arabic) {
15894                         $endedletter = array(1569,1570,1571,1572,1573,1575,1577,1583,1584,1585,1586,1608,1688);
15895                         $alfletter = array(1570,1571,1573,1575);
15896                         $chardata2 = $chardata;
15897                         $laaletter = false;
15898                         $charAL = array();
15899                         $x = 0;
15900                         for ($i=0; $i < $numchars; ++$i) {
15901                                 if (($this->unicode->uni_type[$chardata[$i]['char']] == 'AL') OR ($chardata[$i]['char'] == 32) OR ($chardata[$i]['char'] == 8204)) {
15902                                         $charAL[$x] = $chardata[$i];
15903                                         $charAL[$x]['i'] = $i;
15904                                         $chardata[$i]['x'] = $x;
15905                                         ++$x;
15906                                 }
15907                         }
15908                         $numAL = $x;
15909                         for ($i=0; $i < $numchars; ++$i) {
15910                                 $thischar = $chardata[$i];
15911                                 if ($i > 0) {
15912                                         $prevchar = $chardata[($i-1)];
15913                                 } else {
15914                                         $prevchar = false;
15915                                 }
15916                                 if (($i+1) < $numchars) {
15917                                         $nextchar = $chardata[($i+1)];
15918                                 } else {
15919                                         $nextchar = false;
15920                                 }
15921                                 if ($this->unicode->uni_type[$thischar['char']] == 'AL') {
15922                                         $x = $thischar['x'];
15923                                         if ($x > 0) {
15924                                                 $prevchar = $charAL[($x-1)];
15925                                         } else {
15926                                                 $prevchar = false;
15927                                         }
15928                                         if (($x+1) < $numAL) {
15929                                                 $nextchar = $charAL[($x+1)];
15930                                         } else {
15931                                                 $nextchar = false;
15932                                         }
15933                                         // if laa letter
15934                                         if (($prevchar !== false) AND ($prevchar['char'] == 1604) AND (in_array($thischar['char'], $alfletter))) {
15935                                                 $arabicarr = $this->unicode->uni_laa_array;
15936                                                 $laaletter = true;
15937                                                 if ($x > 1) {
15938                                                         $prevchar = $charAL[($x-2)];
15939                                                 } else {
15940                                                         $prevchar = false;
15941                                                 }
15942                                         } else {
15943                                                 $arabicarr = $this->unicode->uni_arabicsubst;
15944                                                 $laaletter = false;
15945                                         }
15946                                         if (($prevchar !== false) AND ($nextchar !== false) AND
15947                                                 (($this->unicode->uni_type[$prevchar['char']] == 'AL') OR ($this->unicode->uni_type[$prevchar['char']] == 'NSM')) AND
15948                                                 (($this->unicode->uni_type[$nextchar['char']] == 'AL') OR ($this->unicode->uni_type[$nextchar['char']] == 'NSM')) AND
15949                                                 ($prevchar['type'] == $thischar['type']) AND
15950                                                 ($nextchar['type'] == $thischar['type']) AND
15951                                                 ($nextchar['char'] != 1567)) {
15952                                                 if (in_array($prevchar['char'], $endedletter)) {
15953                                                         if (isset($arabicarr[$thischar['char']][2])) {
15954                                                                 // initial
15955                                                                 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
15956                                                         }
15957                                                 } else {
15958                                                         if (isset($arabicarr[$thischar['char']][3])) {
15959                                                                 // medial
15960                                                                 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][3];
15961                                                         }
15962                                                 }
15963                                         } elseif (($nextchar !== false) AND
15964                                                 (($this->unicode->uni_type[$nextchar['char']] == 'AL') OR ($this->unicode->uni_type[$nextchar['char']] == 'NSM')) AND
15965                                                 ($nextchar['type'] == $thischar['type']) AND
15966                                                 ($nextchar['char'] != 1567)) {
15967                                                 if (isset($arabicarr[$chardata[$i]['char']][2])) {
15968                                                         // initial
15969                                                         $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
15970                                                 }
15971                                         } elseif ((($prevchar !== false) AND
15972                                                 (($this->unicode->uni_type[$prevchar['char']] == 'AL') OR ($this->unicode->uni_type[$prevchar['char']] == 'NSM')) AND
15973                                                 ($prevchar['type'] == $thischar['type'])) OR
15974                                                 (($nextchar !== false) AND ($nextchar['char'] == 1567))) {
15975                                                 // final
15976                                                 if (($i > 1) AND ($thischar['char'] == 1607) AND
15977                                                         ($chardata[$i-1]['char'] == 1604) AND
15978                                                         ($chardata[$i-2]['char'] == 1604)) {
15979                                                         //Allah Word
15980                                                         // mark characters to delete with false
15981                                                         $chardata2[$i-2]['char'] = false;
15982                                                         $chardata2[$i-1]['char'] = false;
15983                                                         $chardata2[$i]['char'] = 65010;
15984                                                 } else {
15985                                                         if (($prevchar !== false) AND in_array($prevchar['char'], $endedletter)) {
15986                                                                 if (isset($arabicarr[$thischar['char']][0])) {
15987                                                                         // isolated
15988                                                                         $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
15989                                                                 }
15990                                                         } else {
15991                                                                 if (isset($arabicarr[$thischar['char']][1])) {
15992                                                                         // final
15993                                                                         $chardata2[$i]['char'] = $arabicarr[$thischar['char']][1];
15994                                                                 }
15995                                                         }
15996                                                 }
15997                                         } elseif (isset($arabicarr[$thischar['char']][0])) {
15998                                                 // isolated
15999                                                 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
16000                                         }
16001                                         // if laa letter
16002                                         if ($laaletter) {
16003                                                 // mark characters to delete with false
16004                                                 $chardata2[($charAL[($x-1)]['i'])]['char'] = false;
16005                                         }
16006                                 } // end if AL (Arabic Letter)
16007                         } // end for each char
16008                         /*
16009                          * Combining characters that can occur with Arabic Shadda (0651 HEX, 1617 DEC) are replaced.
16010                          * Putting the combining mark and shadda in the same glyph allows us to avoid the two marks overlapping each other in an illegible manner.
16011                          */
16012                         for ($i = 0; $i < ($numchars-1); ++$i) {
16013                                 if (($chardata2[$i]['char'] == 1617) AND (isset($this->unicode->uni_diacritics[($chardata2[$i+1]['char'])]))) {
16014                                         // check if the subtitution font is defined on current font
16015                                         if (isset($this->CurrentFont['cw'][($this->unicode->uni_diacritics[($chardata2[$i+1]['char'])])])) {
16016                                                 $chardata2[$i]['char'] = false;
16017                                                 $chardata2[$i+1]['char'] = $this->unicode->uni_diacritics[($chardata2[$i+1]['char'])];
16018                                         }
16019                                 }
16020                         }
16021                         // remove marked characters
16022                         foreach ($chardata2 as $key => $value) {
16023                                 if ($value['char'] === false) {
16024                                         unset($chardata2[$key]);
16025                                 }
16026                         }
16027                         $chardata = array_values($chardata2);
16028                         $numchars = count($chardata);
16029                         unset($chardata2);
16030                         unset($arabicarr);
16031                         unset($laaletter);
16032                         unset($charAL);
16033                 }
16034 
16035                 // 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.
16036                 for ($j=$maxlevel; $j > 0; $j--) {
16037                         $ordarray = Array();
16038                         $revarr = Array();
16039                         $onlevel = false;
16040                         for ($i=0; $i < $numchars; ++$i) {
16041                                 if ($chardata[$i]['level'] >= $j) {
16042                                         $onlevel = true;
16043                                         if (isset($this->unicode->uni_mirror[$chardata[$i]['char']])) {
16044                                                 // 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.
16045                                                 $chardata[$i]['char'] = $this->unicode->uni_mirror[$chardata[$i]['char']];
16046                                         }
16047                                         $revarr[] = $chardata[$i];
16048                                 } else {
16049                                         if ($onlevel) {
16050                                                 $revarr = array_reverse($revarr);
16051                                                 $ordarray = array_merge($ordarray, $revarr);
16052                                                 $revarr = Array();
16053                                                 $onlevel = false;
16054                                         }
16055                                         $ordarray[] = $chardata[$i];
16056                                 }
16057                         }
16058                         if ($onlevel) {
16059                                 $revarr = array_reverse($revarr);
16060                                 $ordarray = array_merge($ordarray, $revarr);
16061                         }
16062                         $chardata = $ordarray;
16063                 }
16064 
16065                 $ordarray = array();
16066                 for ($i=0; $i < $numchars; ++$i) {
16067                         $ordarray[] = $chardata[$i]['char'];
16068                         // store char values for subsetting
16069                         $this->CurrentFont['subsetchars'][$chardata[$i]['char']] = true;
16070                 }
16071                 // update font subsetchars
16072                 $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
16073                 return $ordarray;
16074         }
16075 
16076         // END OF BIDIRECTIONAL TEXT SECTION -------------------
16077 
16086         protected function encodeNameObject($name) {
16087                 $escname = '';
16088                 $length = strlen($name);
16089                 for ($i = 0; $i < $length; ++$i) {
16090                         $chr = $name{$i};
16091                         if (preg_match('/[0-9a-zA-Z]/', $chr) == 1) {
16092                                 $escname .= $chr;
16093                         } else {
16094                                 $escname .= sprintf('#%02X', ord($chr));
16095                         }
16096                 }
16097                 return $escname;
16098         }
16099 
16111         public function setDestination($name, $y=-1, $page='') {
16112                 // remove unsupported characters
16113                 $name = $this->encodeNameObject($name);
16114                 if ($this->empty_string($name)) {
16115                         return false;
16116                 }
16117                 if ($y == -1) {
16118                         $y = $this->GetY();
16119                 }
16120                 if (empty($page)) {
16121                         $page = $this->PageNo();
16122                         if (empty($page)) {
16123                                 return;
16124                         }
16125                 }
16126                 $this->dests[$name] = array('y' => $y, 'p' => $page);
16127                 return $name;
16128         }
16129 
16137         public function getDestination() {
16138                 return $this->dests;
16139         }
16140 
16147         protected function _putdests() {
16148                 if (empty($this->dests)) {
16149                         return;
16150                 }
16151                 $this->n_dests = $this->_newobj();
16152                 $out = ' <<';
16153                 foreach($this->dests as $name => $o) {
16154                         $out .= ' /'.$name.' '.sprintf('[%u 0 R /XYZ 0 %.2F null]', $this->page_obj_id[($o['p'])], ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k)));
16155                 }
16156                 $out .= ' >>';
16157                 $out .= "\n".'endobj';
16158                 $this->_out($out);
16159         }
16160 
16171         public function setBookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0)) {
16172                 $this->Bookmark($txt, $level, $y, $page, $style, $color);
16173         }
16174 
16187         public function Bookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0)) {
16188                 if ($level < 0) {
16189                         $level = 0;
16190                 }
16191                 if (isset($this->outlines[0])) {
16192                         $lastoutline = end($this->outlines);
16193                         $maxlevel = $lastoutline['l'] + 1;
16194                 } else {
16195                         $maxlevel = 0;
16196                 }
16197                 if ($level > $maxlevel) {
16198                         $level = $maxlevel;
16199                 }
16200                 if ($y == -1) {
16201                         $y = $this->GetY();
16202                 }
16203                 if (empty($page)) {
16204                         $page = $this->PageNo();
16205                         if (empty($page)) {
16206                                 return;
16207                         }
16208                 }
16209                 $this->outlines[] = array('t' => $txt, 'l' => $level, 'y' => $y, 'p' => $page, 's' => strtoupper($style), 'c' => $color);
16210         }
16211 
16217         protected function sortBookmarks() {
16218                 // get sorting columns
16219                 $outline_p = array();
16220                 $outline_y = array();
16221                 foreach ($this->outlines as $key => $row) {
16222                         $outline_p[$key] = $row['p'];
16223                         $outline_k[$key] = $key;
16224                 }
16225                 // sort outlines by page and original position
16226                 array_multisort($outline_p, SORT_NUMERIC, SORT_ASC, $outline_k, SORT_NUMERIC, SORT_ASC, $this->outlines);
16227         }
16228 
16235         protected function _putbookmarks() {
16236                 $nb = count($this->outlines);
16237                 if ($nb == 0) {
16238                         return;
16239                 }
16240                 // sort bookmarks
16241                 $this->sortBookmarks();
16242                 $lru = array();
16243                 $level = 0;
16244                 foreach ($this->outlines as $i => $o) {
16245                         if ($o['l'] > 0) {
16246                                 $parent = $lru[($o['l'] - 1)];
16247                                 //Set parent and last pointers
16248                                 $this->outlines[$i]['parent'] = $parent;
16249                                 $this->outlines[$parent]['last'] = $i;
16250                                 if ($o['l'] > $level) {
16251                                         //Level increasing: set first pointer
16252                                         $this->outlines[$parent]['first'] = $i;
16253                                 }
16254                         } else {
16255                                 $this->outlines[$i]['parent'] = $nb;
16256                         }
16257                         if (($o['l'] <= $level) AND ($i > 0)) {
16258                                 //Set prev and next pointers
16259                                 $prev = $lru[$o['l']];
16260                                 $this->outlines[$prev]['next'] = $i;
16261                                 $this->outlines[$i]['prev'] = $prev;
16262                         }
16263                         $lru[$o['l']] = $i;
16264                         $level = $o['l'];
16265                 }
16266                 //Outline items
16267                 $n = $this->n + 1;
16268                 $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';
16269                 foreach ($this->outlines as $i => $o) {
16270                         $oid = $this->_newobj();
16271                         // covert HTML title to string
16272                         $title = preg_replace($nltags, "\n", $o['t']);
16273                         $title = preg_replace("/[\r]+/si", '', $title);
16274                         $title = preg_replace("/[\n]+/si", "\n", $title);
16275                         $title = strip_tags($title);
16276                         $title = $this->stringTrim($title);
16277                         $out = '<</Title '.$this->_textstring($title, $oid);
16278                         $out .= ' /Parent '.($n + $o['parent']).' 0 R';
16279                         if (isset($o['prev'])) {
16280                                 $out .= ' /Prev '.($n + $o['prev']).' 0 R';
16281                         }
16282                         if (isset($o['next'])) {
16283                                 $out .= ' /Next '.($n + $o['next']).' 0 R';
16284                         }
16285                         if (isset($o['first'])) {
16286                                 $out .= ' /First '.($n + $o['first']).' 0 R';
16287                         }
16288                         if (isset($o['last'])) {
16289                                 $out .= ' /Last '.($n + $o['last']).' 0 R';
16290                         }
16291                         if (isset($this->page_obj_id[($o['p'])])) {
16292                                 $out .= ' '.sprintf('/Dest [%u 0 R /XYZ 0 %.2F null]', $this->page_obj_id[($o['p'])], ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k)));
16293                         }
16294                         // set font style
16295                         $style = 0;
16296                         if (!empty($o['s'])) {
16297                                 // bold
16298                                 if (strpos($o['s'], 'B') !== false) {
16299                                         $style |= 2;
16300                                 }
16301                                 // oblique
16302                                 if (strpos($o['s'], 'I') !== false) {
16303                                         $style |= 1;
16304                                 }
16305                         }
16306                         $out .= sprintf(' /F %d', $style);
16307                         // set bookmark color
16308                         if (isset($o['c']) AND is_array($o['c']) AND (count($o['c']) == 3)) {
16309                                 $color = array_values($o['c']);
16310                                 $out .= sprintf(' /C [%.3F %.3F %.3F]', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255));
16311                         } else {
16312                                 // black
16313                                 $out .= ' /C [0.0 0.0 0.0]';
16314                         }
16315                         $out .= ' /Count 0'; // normally closed item
16316                         $out .= ' >>';
16317                         $out .= "\n".'endobj';
16318                         $this->_out($out);
16319                 }
16320                 //Outline root
16321                 $this->OutlineRoot = $this->_newobj();
16322                 $this->_out('<< /Type /Outlines /First '.$n.' 0 R /Last '.($n + $lru[0]).' 0 R >>'."\n".'endobj');
16323         }
16324 
16325         // --- JAVASCRIPT ------------------------------------------------------
16326 
16334         public function IncludeJS($script) {
16335                 $this->javascript .= $script;
16336         }
16337 
16347         public function addJavascriptObject($script, $onload=false) {
16348                 if ($this->pdfa_mode) {
16349                         // javascript is not allowed in PDF/A mode
16350                         return false;
16351                 }
16352                 ++$this->n;
16353                 $this->js_objects[$this->n] = array('n' => $this->n, 'js' => $script, 'onload' => $onload);
16354                 return $this->n;
16355         }
16356 
16363         protected function _putjavascript() {
16364                 if ($this->pdfa_mode OR (empty($this->javascript) AND empty($this->js_objects))) {
16365                         return;
16366                 }
16367                 if (strpos($this->javascript, 'this.addField') > 0) {
16368                         if (!$this->ur['enabled']) {
16369                                 //$this->setUserRights();
16370                         }
16371                         // the following two lines are used to avoid form fields duplication after saving
16372                         // The addField method only works when releasing user rights (UR3)
16373                         $jsa = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%.2F,%.2F,%.2F,%.2F]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1);
16374                         $jsb = "getField('tcpdfdocsaved').value='saved';";
16375                         $this->javascript = $jsa."\n".$this->javascript."\n".$jsb;
16376                 }
16377                 $this->n_js = $this->_newobj();
16378                 $out = ' << /Names [';
16379                 if (!empty($this->javascript)) {
16380                         $out .= ' (EmbeddedJS) '.($this->n + 1).' 0 R';
16381                 }
16382                 if (!empty($this->js_objects)) {
16383                         foreach ($this->js_objects as $key => $val) {
16384                                 if ($val['onload']) {
16385                                         $out .= ' (JS'.$key.') '.$key.' 0 R';
16386                                 }
16387                         }
16388                 }
16389                 $out .= ' ] >>';
16390                 $out .= "\n".'endobj';
16391                 $this->_out($out);
16392                 // default Javascript object
16393                 if (!empty($this->javascript)) {
16394                         $obj_id = $this->_newobj();
16395                         $out = '<< /S /JavaScript';
16396                         $out .= ' /JS '.$this->_textstring($this->javascript, $obj_id);
16397                         $out .= ' >>';
16398                         $out .= "\n".'endobj';
16399                         $this->_out($out);
16400                 }
16401                 // additional Javascript objects
16402                 if (!empty($this->js_objects)) {
16403                         foreach ($this->js_objects as $key => $val) {
16404                                 $out = $this->_getobj($key)."\n".' << /S /JavaScript /JS '.$this->_textstring($val['js'], $key).' >>'."\n".'endobj';
16405                                 $this->_out($out);
16406                         }
16407                 }
16408         }
16409 
16417         protected function _JScolor($color) {
16418                 static $aColors = array('transparent', 'black', 'white', 'red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'dkGray', 'gray', 'ltGray');
16419                 if (substr($color,0,1) == '#') {
16420                         return sprintf("['RGB',%.3F,%.3F,%.3F]", hexdec(substr($color,1,2))/255, hexdec(substr($color,3,2))/255, hexdec(substr($color,5,2))/255);
16421                 }
16422                 if (!in_array($color,$aColors)) {
16423                         $this->Error('Invalid color: '.$color);
16424                 }
16425                 return 'color.'.$color;
16426         }
16427 
16441         protected function _addfield($type, $name, $x, $y, $w, $h, $prop) {
16442                 if ($this->rtl) {
16443                         $x = $x - $w;
16444                 }
16445                 // the followind avoid fields duplication after saving the document
16446                 $this->javascript .= "if (getField('tcpdfdocsaved').value != 'saved') {";
16447                 $k = $this->k;
16448                 $this->javascript .= sprintf("f".$name."=this.addField('%s','%s',%u,[%.2F,%.2F,%.2F,%.2F]);", $name, $type, $this->PageNo()-1, $x*$k, ($this->h-$y)*$k+1, ($x+$w)*$k, ($this->h-$y-$h)*$k+1)."\n";
16449                 $this->javascript .= 'f'.$name.'.textSize='.$this->FontSizePt.";\n";
16450                 while (list($key, $val) = each($prop)) {
16451                         if (strcmp(substr($key, -5), 'Color') == 0) {
16452                                 $val = $this->_JScolor($val);
16453                         } else {
16454                                 $val = "'".$val."'";
16455                         }
16456                         $this->javascript .= 'f'.$name.'.'.$key.'='.$val.";\n";
16457                 }
16458                 if ($this->rtl) {
16459                         $this->x -= $w;
16460                 } else {
16461                         $this->x += $w;
16462                 }
16463                 $this->javascript .= '}';
16464         }
16465 
16466         // --- FORM FIELDS -----------------------------------------------------
16467 
16476         protected function getAnnotOptFromJSProp($prop) {
16477                 if (isset($prop['aopt']) AND is_array($prop['aopt'])) {
16478                         // the annotation options area lready defined
16479                         return $prop['aopt'];
16480                 }
16481                 $opt = array(); // value to be returned
16482                 // alignment: Controls how the text is laid out within the text field.
16483                 if (isset($prop['alignment'])) {
16484                         switch ($prop['alignment']) {
16485                                 case 'left': {
16486                                         $opt['q'] = 0;
16487                                         break;
16488                                 }
16489                                 case 'center': {
16490                                         $opt['q'] = 1;
16491                                         break;
16492                                 }
16493                                 case 'right': {
16494                                         $opt['q'] = 2;
16495                                         break;
16496                                 }
16497                                 default: {
16498                                         $opt['q'] = ($this->rtl)?2:0;
16499                                         break;
16500                                 }
16501                         }
16502                 }
16503                 // lineWidth: Specifies the thickness of the border when stroking the perimeter of a field's rectangle.
16504                 if (isset($prop['lineWidth'])) {
16505                         $linewidth = intval($prop['lineWidth']);
16506                 } else {
16507                         $linewidth = 1;
16508                 }
16509                 // borderStyle: The border style for a field.
16510                 if (isset($prop['borderStyle'])) {
16511                         switch ($prop['borderStyle']) {
16512                                 case 'border.d':
16513                                 case 'dashed': {
16514                                         $opt['border'] = array(0, 0, $linewidth, array(3, 2));
16515                                         $opt['bs'] = array('w'=>$linewidth, 's'=>'D', 'd'=>array(3, 2));
16516                                         break;
16517                                 }
16518                                 case 'border.b':
16519                                 case 'beveled': {
16520                                         $opt['border'] = array(0, 0, $linewidth);
16521                                         $opt['bs'] = array('w'=>$linewidth, 's'=>'B');
16522                                         break;
16523                                 }
16524                                 case 'border.i':
16525                                 case 'inset': {
16526                                         $opt['border'] = array(0, 0, $linewidth);
16527                                         $opt['bs'] = array('w'=>$linewidth, 's'=>'I');
16528                                         break;
16529                                 }
16530                                 case 'border.u':
16531                                 case 'underline': {
16532                                         $opt['border'] = array(0, 0, $linewidth);
16533                                         $opt['bs'] = array('w'=>$linewidth, 's'=>'U');
16534                                         break;
16535                                 }
16536                                 case 'border.s':
16537                                 case 'solid': {
16538                                         $opt['border'] = array(0, 0, $linewidth);
16539                                         $opt['bs'] = array('w'=>$linewidth, 's'=>'S');
16540                                         break;
16541                                 }
16542                                 default: {
16543                                         break;
16544                                 }
16545                         }
16546                 }
16547                 if (isset($prop['border']) AND is_array($prop['border'])) {
16548                         $opt['border'] = $prop['border'];
16549                 }
16550                 if (!isset($opt['mk'])) {
16551                         $opt['mk'] = array();
16552                 }
16553                 if (!isset($opt['mk']['if'])) {
16554                         $opt['mk']['if'] = array();
16555                 }
16556                 $opt['mk']['if']['a'] = array(0.5, 0.5);
16557                 // buttonAlignX: Controls how space is distributed from the left of the button face with respect to the icon.
16558                 if (isset($prop['buttonAlignX'])) {
16559                         $opt['mk']['if']['a'][0] = $prop['buttonAlignX'];
16560                 }
16561                 // buttonAlignY: Controls how unused space is distributed from the bottom of the button face with respect to the icon.
16562                 if (isset($prop['buttonAlignY'])) {
16563                         $opt['mk']['if']['a'][1] = $prop['buttonAlignY'];
16564                 }
16565                 // buttonFitBounds: If true, the extent to which the icon may be scaled is set to the bounds of the button field.
16566                 if (isset($prop['buttonFitBounds']) AND ($prop['buttonFitBounds'] == 'true')) {
16567                         $opt['mk']['if']['fb'] = true;
16568                 }
16569                 // buttonScaleHow: Controls how the icon is scaled (if necessary) to fit inside the button face.
16570                 if (isset($prop['buttonScaleHow'])) {
16571                         switch ($prop['buttonScaleHow']) {
16572                                 case 'scaleHow.proportional': {
16573                                         $opt['mk']['if']['s'] = 'P';
16574                                         break;
16575                                 }
16576                                 case 'scaleHow.anamorphic': {
16577                                         $opt['mk']['if']['s'] = 'A';
16578                                         break;
16579                                 }
16580                         }
16581                 }
16582                 // buttonScaleWhen: Controls when an icon is scaled to fit inside the button face.
16583                 if (isset($prop['buttonScaleWhen'])) {
16584                         switch ($prop['buttonScaleWhen']) {
16585                                 case 'scaleWhen.always': {
16586                                         $opt['mk']['if']['sw'] = 'A';
16587                                         break;
16588                                 }
16589                                 case 'scaleWhen.never': {
16590                                         $opt['mk']['if']['sw'] = 'N';
16591                                         break;
16592                                 }
16593                                 case 'scaleWhen.tooBig': {
16594                                         $opt['mk']['if']['sw'] = 'B';
16595                                         break;
16596                                 }
16597                                 case 'scaleWhen.tooSmall': {
16598                                         $opt['mk']['if']['sw'] = 'S';
16599                                         break;
16600                                 }
16601                         }
16602                 }
16603                 // buttonPosition: Controls how the text and the icon of the button are positioned with respect to each other within the button face.
16604                 if (isset($prop['buttonPosition'])) {
16605                         switch ($prop['buttonPosition']) {
16606                                 case 0:
16607                                 case 'position.textOnly': {
16608                                         $opt['mk']['tp'] = 0;
16609                                         break;
16610                                 }
16611                                 case 1:
16612                                 case 'position.iconOnly': {
16613                                         $opt['mk']['tp'] = 1;
16614                                         break;
16615                                 }
16616                                 case 2:
16617                                 case 'position.iconTextV': {
16618                                         $opt['mk']['tp'] = 2;
16619                                         break;
16620                                 }
16621                                 case 3:
16622                                 case 'position.textIconV': {
16623                                         $opt['mk']['tp'] = 3;
16624                                         break;
16625                                 }
16626                                 case 4:
16627                                 case 'position.iconTextH': {
16628                                         $opt['mk']['tp'] = 4;
16629                                         break;
16630                                 }
16631                                 case 5:
16632                                 case 'position.textIconH': {
16633                                         $opt['mk']['tp'] = 5;
16634                                         break;
16635                                 }
16636                                 case 6:
16637                                 case 'position.overlay': {
16638                                         $opt['mk']['tp'] = 6;
16639                                         break;
16640                                 }
16641                         }
16642                 }
16643                 // fillColor: Specifies the background color for a field.
16644                 if (isset($prop['fillColor'])) {
16645                         if (is_array($prop['fillColor'])) {
16646                                 $opt['mk']['bg'] = $prop['fillColor'];
16647                         } else {
16648                                 $opt['mk']['bg'] = $this->convertHTMLColorToDec($prop['fillColor']);
16649                         }
16650                 }
16651                 // 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.
16652                 if (isset($prop['strokeColor'])) {
16653                         if (is_array($prop['strokeColor'])) {
16654                                 $opt['mk']['bc'] = $prop['strokeColor'];
16655                         } else {
16656                                 $opt['mk']['bc'] = $this->convertHTMLColorToDec($prop['strokeColor']);
16657                         }
16658                 }
16659                 // rotation: The rotation of a widget in counterclockwise increments.
16660                 if (isset($prop['rotation'])) {
16661                         $opt['mk']['r'] = $prop['rotation'];
16662                 }
16663                 // charLimit: Limits the number of characters that a user can type into a text field.
16664                 if (isset($prop['charLimit'])) {
16665                         $opt['maxlen'] = intval($prop['charLimit']);
16666                 }
16667                 if (!isset($ff)) {
16668                         $ff = 0; // default value
16669                 }
16670                 // readonly: The read-only characteristic of a field. If a field is read-only, the user can see the field but cannot change it.
16671                 if (isset($prop['readonly']) AND ($prop['readonly'] == 'true')) {
16672                         $ff += 1 << 0;
16673                 }
16674                 // required: Specifies whether a field requires a value.
16675                 if (isset($prop['required']) AND ($prop['required'] == 'true')) {
16676                         $ff += 1 << 1;
16677                 }
16678                 // multiline: Controls how text is wrapped within the field.
16679                 if (isset($prop['multiline']) AND ($prop['multiline'] == 'true')) {
16680                         $ff += 1 << 12;
16681                 }
16682                 // password: Specifies whether the field should display asterisks when data is entered in the field.
16683                 if (isset($prop['password']) AND ($prop['password'] == 'true')) {
16684                         $ff += 1 << 13;
16685                 }
16686                 // NoToggleToOff: If set, exactly one radio button shall be selected at all times; selecting the currently selected button has no effect.
16687                 if (isset($prop['NoToggleToOff']) AND ($prop['NoToggleToOff'] == 'true')) {
16688                         $ff += 1 << 14;
16689                 }
16690                 // Radio: If set, the field is a set of radio buttons.
16691                 if (isset($prop['Radio']) AND ($prop['Radio'] == 'true')) {
16692                         $ff += 1 << 15;
16693                 }
16694                 // Pushbutton: If set, the field is a pushbutton that does not retain a permanent value.
16695                 if (isset($prop['Pushbutton']) AND ($prop['Pushbutton'] == 'true')) {
16696                         $ff += 1 << 16;
16697                 }
16698                 // Combo: If set, the field is a combo box; if clear, the field is a list box.
16699                 if (isset($prop['Combo']) AND ($prop['Combo'] == 'true')) {
16700                         $ff += 1 << 17;
16701                 }
16702                 // editable: Controls whether a combo box is editable.
16703                 if (isset($prop['editable']) AND ($prop['editable'] == 'true')) {
16704                         $ff += 1 << 18;
16705                 }
16706                 // Sort: If set, the field's option items shall be sorted alphabetically.
16707                 if (isset($prop['Sort']) AND ($prop['Sort'] == 'true')) {
16708                         $ff += 1 << 19;
16709                 }
16710                 // fileSelect: If true, sets the file-select flag in the Options tab of the text field (Field is Used for File Selection).
16711                 if (isset($prop['fileSelect']) AND ($prop['fileSelect'] == 'true')) {
16712                         $ff += 1 << 20;
16713                 }
16714                 // multipleSelection: If true, indicates that a list box allows a multiple selection of items.
16715                 if (isset($prop['multipleSelection']) AND ($prop['multipleSelection'] == 'true')) {
16716                         $ff += 1 << 21;
16717                 }
16718                 // doNotSpellCheck: If true, spell checking is not performed on this editable text field.
16719                 if (isset($prop['doNotSpellCheck']) AND ($prop['doNotSpellCheck'] == 'true')) {
16720                         $ff += 1 << 22;
16721                 }
16722                 // doNotScroll: If true, the text field does not scroll and the user, therefore, is limited by the rectangular region designed for the field.
16723                 if (isset($prop['doNotScroll']) AND ($prop['doNotScroll'] == 'true')) {
16724                         $ff += 1 << 23;
16725                 }
16726                 // 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.
16727                 if (isset($prop['comb']) AND ($prop['comb'] == 'true')) {
16728                         $ff += 1 << 24;
16729                 }
16730                 // 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.
16731                 if (isset($prop['radiosInUnison']) AND ($prop['radiosInUnison'] == 'true')) {
16732                         $ff += 1 << 25;
16733                 }
16734                 // richText: If true, the field allows rich text formatting.
16735                 if (isset($prop['richText']) AND ($prop['richText'] == 'true')) {
16736                         $ff += 1 << 25;
16737                 }
16738                 // commitOnSelChange: Controls whether a field value is committed after a selection change.
16739                 if (isset($prop['commitOnSelChange']) AND ($prop['commitOnSelChange'] == 'true')) {
16740                         $ff += 1 << 26;
16741                 }
16742                 $opt['ff'] = $ff;
16743                 // defaultValue: The default value of a field - that is, the value that the field is set to when the form is reset.
16744                 if (isset($prop['defaultValue'])) {
16745                         $opt['dv'] = $prop['defaultValue'];
16746                 }
16747                 $f = 4; // default value for annotation flags
16748                 // readonly: The read-only characteristic of a field. If a field is read-only, the user can see the field but cannot change it.
16749                 if (isset($prop['readonly']) AND ($prop['readonly'] == 'true')) {
16750                         $f += 1 << 6;
16751                 }
16752                 // display: Controls whether the field is hidden or visible on screen and in print.
16753                 if (isset($prop['display'])) {
16754                         if ($prop['display'] == 'display.visible') {
16755                                 //
16756                         } elseif ($prop['display'] == 'display.hidden') {
16757                                 $f += 1 << 1;
16758                         } elseif ($prop['display'] == 'display.noPrint') {
16759                                 $f -= 1 << 2;
16760                         } elseif ($prop['display'] == 'display.noView') {
16761                                 $f += 1 << 5;
16762                         }
16763                 }
16764                 $opt['f'] = $f;
16765                 // currentValueIndices: Reads and writes single or multiple values of a list box or combo box.
16766                 if (isset($prop['currentValueIndices']) AND is_array($prop['currentValueIndices'])) {
16767                         $opt['i'] = $prop['currentValueIndices'];
16768                 }
16769                 // value: The value of the field data that the user has entered.
16770                 if (isset($prop['value'])) {
16771                         if (is_array($prop['value'])) {
16772                                 $opt['opt'] = array();
16773                                 foreach ($prop['value'] AS $key => $optval) {
16774                                         // exportValues: An array of strings representing the export values for the field.
16775                                         if (isset($prop['exportValues'][$key])) {
16776                                                 $opt['opt'][$key] = array($prop['exportValues'][$key], $prop['value'][$key]);
16777                                         } else {
16778                                                 $opt['opt'][$key] = $prop['value'][$key];
16779                                         }
16780                                 }
16781                         } else {
16782                                 $opt['v'] = $prop['value'];
16783                         }
16784                 }
16785                 // richValue: This property specifies the text contents and formatting of a rich text field.
16786                 if (isset($prop['richValue'])) {
16787                         $opt['rv'] = $prop['richValue'];
16788                 }
16789                 // submitName: If nonempty, used during form submission instead of name. Only applicable if submitting in HTML format (that is, URL-encoded).
16790                 if (isset($prop['submitName'])) {
16791                         $opt['tm'] = $prop['submitName'];
16792                 }
16793                 // name: Fully qualified field name.
16794                 if (isset($prop['name'])) {
16795                         $opt['t'] = $prop['name'];
16796                 }
16797                 // userName: The user name (short description string) of the field.
16798                 if (isset($prop['userName'])) {
16799                         $opt['tu'] = $prop['userName'];
16800                 }
16801                 // highlight: Defines how a button reacts when a user clicks it.
16802                 if (isset($prop['highlight'])) {
16803                         switch ($prop['highlight']) {
16804                                 case 'none':
16805                                 case 'highlight.n': {
16806                                         $opt['h'] = 'N';
16807                                         break;
16808                                 }
16809                                 case 'invert':
16810                                 case 'highlight.i': {
16811                                         $opt['h'] = 'i';
16812                                         break;
16813                                 }
16814                                 case 'push':
16815                                 case 'highlight.p': {
16816                                         $opt['h'] = 'P';
16817                                         break;
16818                                 }
16819                                 case 'outline':
16820                                 case 'highlight.o': {
16821                                         $opt['h'] = 'O';
16822                                         break;
16823                                 }
16824                         }
16825                 }
16826                 // Unsupported options:
16827                 // - calcOrderIndex: Changes the calculation order of fields in the document.
16828                 // - delay: Delays the redrawing of a field's appearance.
16829                 // - defaultStyle: This property defines the default style attributes for the form field.
16830                 // - style: Allows the user to set the glyph style of a check box or radio button.
16831                 // - textColor, textFont, textSize
16832                 return $opt;
16833         }
16834 
16842         public function setFormDefaultProp($prop=array()) {
16843                 $this->default_form_prop = $prop;
16844         }
16845 
16853         public function getFormDefaultProp() {
16854                 return $this->default_form_prop;
16855         }
16856 
16871         public function TextField($name, $w, $h, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
16872                 if ($x === '') {
16873                         $x = $this->x;
16874                 }
16875                 if ($y === '') {
16876                         $y = $this->y;
16877                 }
16878                 // check page for no-write regions and adapt page margins if necessary
16879                 list($x, $y) = $this->checkPageRegions($h, $x, $y);
16880                 if ($js) {
16881                         $this->_addfield('text', $name, $x, $y, $w, $h, $prop);
16882                         return;
16883                 }
16884                 // get default style
16885                 $prop = array_merge($this->getFormDefaultProp(), $prop);
16886                 // get annotation data
16887                 $popt = $this->getAnnotOptFromJSProp($prop);
16888                 // set default appearance stream
16889                 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
16890                 $fontstyle = sprintf('/F%d %.2F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
16891                 $popt['da'] = $fontstyle;
16892                 // build appearance stream
16893                 $popt['ap'] = array();
16894                 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
16895                 $text = '';
16896                 if (isset($prop['value']) AND !empty($prop['value'])) {
16897                         $text = $prop['value'];
16898                 } elseif (isset($opt['v']) AND !empty($opt['v'])) {
16899                         $text = $opt['v'];
16900                 }
16901                 $tmpid = $this->startTemplate($w, $h, false);
16902                 $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
16903                 $this->endTemplate();
16904                 --$this->n;
16905                 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
16906                 unset($this->xobjects[$tmpid]);
16907                 $popt['ap']['n'] .= 'Q EMC';
16908                 // merge options
16909                 $opt = array_merge($popt, $opt);
16910                 // remove some conflicting options
16911                 unset($opt['bs']);
16912                 // set remaining annotation data
16913                 $opt['Subtype'] = 'Widget';
16914                 $opt['ft'] = 'Tx';
16915                 $opt['t'] = $name;
16916                 // Additional annotation's parameters (check _putannotsobj() method):
16917                 //$opt['f']
16918                 //$opt['as']
16919                 //$opt['bs']
16920                 //$opt['be']
16921                 //$opt['c']
16922                 //$opt['border']
16923                 //$opt['h']
16924                 //$opt['mk'];
16925                 //$opt['mk']['r']
16926                 //$opt['mk']['bc'];
16927                 //$opt['mk']['bg'];
16928                 unset($opt['mk']['ca']);
16929                 unset($opt['mk']['rc']);
16930                 unset($opt['mk']['ac']);
16931                 unset($opt['mk']['i']);
16932                 unset($opt['mk']['ri']);
16933                 unset($opt['mk']['ix']);
16934                 unset($opt['mk']['if']);
16935                 //$opt['mk']['if']['sw'];
16936                 //$opt['mk']['if']['s'];
16937                 //$opt['mk']['if']['a'];
16938                 //$opt['mk']['if']['fb'];
16939                 unset($opt['mk']['tp']);
16940                 //$opt['tu']
16941                 //$opt['tm']
16942                 //$opt['ff']
16943                 //$opt['v']
16944                 //$opt['dv']
16945                 //$opt['a']
16946                 //$opt['aa']
16947                 //$opt['q']
16948                 $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
16949                 if ($this->rtl) {
16950                         $this->x -= $w;
16951                 } else {
16952                         $this->x += $w;
16953                 }
16954         }
16955 
16971         public function RadioButton($name, $w, $prop=array(), $opt=array(), $onvalue='On', $checked=false, $x='', $y='', $js=false) {
16972                 if ($x === '') {
16973                         $x = $this->x;
16974                 }
16975                 if ($y === '') {
16976                         $y = $this->y;
16977                 }
16978                 // check page for no-write regions and adapt page margins if necessary
16979                 list($x, $y) = $this->checkPageRegions($w, $x, $y);
16980                 if ($js) {
16981                         $this->_addfield('radiobutton', $name, $x, $y, $w, $w, $prop);
16982                         return;
16983                 }
16984                 if ($this->empty_string($onvalue)) {
16985                         $onvalue = 'On';
16986                 }
16987                 if ($checked) {
16988                         $defval = $onvalue;
16989                 } else {
16990                         $defval = 'Off';
16991                 }
16992                 // set font
16993                 $font = 'zapfdingbats';
16994                 $this->AddFont($font);
16995                 $tmpfont = $this->getFontBuffer($font);
16996                 // set data for parent group
16997                 if (!isset($this->radiobutton_groups[$this->page])) {
16998                         $this->radiobutton_groups[$this->page] = array();
16999                 }
17000                 if (!isset($this->radiobutton_groups[$this->page][$name])) {
17001                         $this->radiobutton_groups[$this->page][$name] = array();
17002                         ++$this->n;
17003                         $this->radiobutton_groups[$this->page][$name]['n'] = $this->n;
17004                         $this->radio_groups[] = $this->n;
17005                 }
17006                 $kid = ($this->n + 1);
17007                 // save object ID to be added on Kids entry on parent object
17008                 $this->radiobutton_groups[$this->page][$name][] = array('kid' => $kid, 'def' => $defval);
17009                 // get default style
17010                 $prop = array_merge($this->getFormDefaultProp(), $prop);
17011                 $prop['NoToggleToOff'] = 'true';
17012                 $prop['Radio'] = 'true';
17013                 $prop['borderStyle'] = 'inset';
17014                 // get annotation data
17015                 $popt = $this->getAnnotOptFromJSProp($prop);
17016                 // set additional default options
17017                 $this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i'];
17018                 $fontstyle = sprintf('/F%d %.2F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor);
17019                 $popt['da'] = $fontstyle;
17020                 // build appearance stream
17021                 $popt['ap'] = array();
17022                 $popt['ap']['n'] = array();
17023                 $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k);
17024                 $popt['ap']['n'][$onvalue] = sprintf('q %s BT /F%d %.2F Tf %.2F %.2F Td ('.chr(108).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, 0, $fy);
17025                 $popt['ap']['n']['Off'] = sprintf('q %s BT /F%d %.2F Tf %.2F %.2F Td ('.chr(109).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, 0, $fy);
17026                 if (!isset($popt['mk'])) {
17027                         $popt['mk'] = array();
17028                 }
17029                 $popt['mk']['ca'] = '(l)';
17030                 // merge options
17031                 $opt = array_merge($popt, $opt);
17032                 // set remaining annotation data
17033                 $opt['Subtype'] = 'Widget';
17034                 $opt['ft'] = 'Btn';
17035                 if ($checked) {
17036                         $opt['v'] = array('/'.$onvalue);
17037                         $opt['as'] = $onvalue;
17038                 } else {
17039                         $opt['as'] = 'Off';
17040                 }
17041                 // store readonly flag
17042                 if (!isset($this->radiobutton_groups[$this->page][$name]['#readonly#'])) {
17043                         $this->radiobutton_groups[$this->page][$name]['#readonly#'] = false;
17044                 }
17045                 $this->radiobutton_groups[$this->page][$name]['#readonly#'] |= ($opt['f'] & 64);
17046                 $this->Annotation($x, $y, $w, $w, $name, $opt, 0);
17047                 if ($this->rtl) {
17048                         $this->x -= $w;
17049                 } else {
17050                         $this->x += $w;
17051                 }
17052         }
17053 
17069         public function ListBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
17070                 if ($x === '') {
17071                         $x = $this->x;
17072                 }
17073                 if ($y === '') {
17074                         $y = $this->y;
17075                 }
17076                 // check page for no-write regions and adapt page margins if necessary
17077                 list($x, $y) = $this->checkPageRegions($h, $x, $y);
17078                 if ($js) {
17079                         $this->_addfield('listbox', $name, $x, $y, $w, $h, $prop);
17080                         $s = '';
17081                         foreach ($values as $value) {
17082                                 $s .= '\''.addslashes($value).'\',';
17083                         }
17084                         $this->javascript .= 'f'.$name.'.setItems(['.substr($s, 0, -1)."]);\n";
17085                         return;
17086                 }
17087                 // get default style
17088                 $prop = array_merge($this->getFormDefaultProp(), $prop);
17089                 // get annotation data
17090                 $popt = $this->getAnnotOptFromJSProp($prop);
17091                 // set additional default values
17092                 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
17093                 $fontstyle = sprintf('/F%d %.2F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
17094                 $popt['da'] = $fontstyle;
17095                 // build appearance stream
17096                 $popt['ap'] = array();
17097                 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
17098                 $text = '';
17099                 foreach($values as $item) {
17100                         $text .= $item."\n";
17101                 }
17102                 $tmpid = $this->startTemplate($w, $h, false);
17103                 $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
17104                 $this->endTemplate();
17105                 --$this->n;
17106                 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
17107                 unset($this->xobjects[$tmpid]);
17108                 $popt['ap']['n'] .= 'Q EMC';
17109                 // merge options
17110                 $opt = array_merge($popt, $opt);
17111                 // set remaining annotation data
17112                 $opt['Subtype'] = 'Widget';
17113                 $opt['ft'] = 'Ch';
17114                 $opt['t'] = $name;
17115                 $opt['opt'] = $values;
17116                 unset($opt['mk']['ca']);
17117                 unset($opt['mk']['rc']);
17118                 unset($opt['mk']['ac']);
17119                 unset($opt['mk']['i']);
17120                 unset($opt['mk']['ri']);
17121                 unset($opt['mk']['ix']);
17122                 unset($opt['mk']['if']);
17123                 unset($opt['mk']['tp']);
17124                 $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
17125                 if ($this->rtl) {
17126                         $this->x -= $w;
17127                 } else {
17128                         $this->x += $w;
17129                 }
17130         }
17131 
17147         public function ComboBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
17148                 if ($x === '') {
17149                         $x = $this->x;
17150                 }
17151                 if ($y === '') {
17152                         $y = $this->y;
17153                 }
17154                 // check page for no-write regions and adapt page margins if necessary
17155                 list($x, $y) = $this->checkPageRegions($h, $x, $y);
17156                 if ($js) {
17157                         $this->_addfield('combobox', $name, $x, $y, $w, $h, $prop);
17158                         $s = '';
17159                         foreach ($values as $value) {
17160                                 $s .= "'".addslashes($value)."',";
17161                         }
17162                         $this->javascript .= 'f'.$name.'.setItems(['.substr($s, 0, -1)."]);\n";
17163                         return;
17164                 }
17165                 // get default style
17166                 $prop = array_merge($this->getFormDefaultProp(), $prop);
17167                 $prop['Combo'] = true;
17168                 // get annotation data
17169                 $popt = $this->getAnnotOptFromJSProp($prop);
17170                 // set additional default options
17171                 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
17172                 $fontstyle = sprintf('/F%d %.2F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
17173                 $popt['da'] = $fontstyle;
17174                 // build appearance stream
17175                 $popt['ap'] = array();
17176                 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
17177                 $text = '';
17178                 foreach($values as $item) {
17179                         $text .= $item[1]."\n";
17180                 }
17181                 $tmpid = $this->startTemplate($w, $h, false);
17182                 $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
17183                 $this->endTemplate();
17184                 --$this->n;
17185                 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
17186                 unset($this->xobjects[$tmpid]);
17187                 $popt['ap']['n'] .= 'Q EMC';
17188                 // merge options
17189                 $opt = array_merge($popt, $opt);
17190                 // set remaining annotation data
17191                 $opt['Subtype'] = 'Widget';
17192                 $opt['ft'] = 'Ch';
17193                 $opt['t'] = $name;
17194                 $opt['opt'] = $values;
17195                 unset($opt['mk']['ca']);
17196                 unset($opt['mk']['rc']);
17197                 unset($opt['mk']['ac']);
17198                 unset($opt['mk']['i']);
17199                 unset($opt['mk']['ri']);
17200                 unset($opt['mk']['ix']);
17201                 unset($opt['mk']['if']);
17202                 unset($opt['mk']['tp']);
17203                 $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
17204                 if ($this->rtl) {
17205                         $this->x -= $w;
17206                 } else {
17207                         $this->x += $w;
17208                 }
17209         }
17210 
17226         public function CheckBox($name, $w, $checked=false, $prop=array(), $opt=array(), $onvalue='Yes', $x='', $y='', $js=false) {
17227                 if ($x === '') {
17228                         $x = $this->x;
17229                 }
17230                 if ($y === '') {
17231                         $y = $this->y;
17232                 }
17233                 // check page for no-write regions and adapt page margins if necessary
17234                 list($x, $y) = $this->checkPageRegions($w, $x, $y);
17235                 if ($js) {
17236                         $this->_addfield('checkbox', $name, $x, $y, $w, $w, $prop);
17237                         return;
17238                 }
17239                 if (!isset($prop['value'])) {
17240                         $prop['value'] = array('Yes');
17241                 }
17242                 // get default style
17243                 $prop = array_merge($this->getFormDefaultProp(), $prop);
17244                 $prop['borderStyle'] = 'inset';
17245                 // get annotation data
17246                 $popt = $this->getAnnotOptFromJSProp($prop);
17247                 // set additional default options
17248                 $font = 'zapfdingbats';
17249                 $this->AddFont($font);
17250                 $tmpfont = $this->getFontBuffer($font);
17251                 $this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i'];
17252                 $fontstyle = sprintf('/F%d %.2F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor);
17253                 $popt['da'] = $fontstyle;
17254                 // build appearance stream
17255                 $popt['ap'] = array();
17256                 $popt['ap']['n'] = array();
17257                 $fy = ((($tmpfont['desc']['Ascent'] + $tmpfont['desc']['Descent']) * $this->FontSizePt) / (1000 * $this->k));
17258                 $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k);
17259                 $popt['ap']['n']['Yes'] = sprintf('q %s BT /F%d %.2F Tf %.2F %.2F Td ('.chr(110).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, 0, $fy);
17260                 $popt['ap']['n']['Off'] = sprintf('q %s BT /F%d %.2F Tf %.2F %.2F Td ('.chr(111).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, 0, $fy);
17261                 // merge options
17262                 $opt = array_merge($popt, $opt);
17263                 // set remaining annotation data
17264                 $opt['Subtype'] = 'Widget';
17265                 $opt['ft'] = 'Btn';
17266                 $opt['t'] = $name;
17267                 $opt['opt'] = array($onvalue);
17268                 if ($checked) {
17269                         $opt['v'] = array('/Yes');
17270                         $opt['as'] = 'Yes';
17271                 } else {
17272                         $opt['v'] = array('/Off');
17273                         $opt['as'] = 'Off';
17274                 }
17275                 $this->Annotation($x, $y, $w, $w, $name, $opt, 0);
17276                 if ($this->rtl) {
17277                         $this->x -= $w;
17278                 } else {
17279                         $this->x += $w;
17280                 }
17281         }
17282 
17299         public function Button($name, $w, $h, $caption, $action, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
17300                 if ($x === '') {
17301                         $x = $this->x;
17302                 }
17303                 if ($y === '') {
17304                         $y = $this->y;
17305                 }
17306                 // check page for no-write regions and adapt page margins if necessary
17307                 list($x, $y) = $this->checkPageRegions($h, $x, $y);
17308                 if ($js) {
17309                         $this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop);
17310                         $this->javascript .= 'f'.$name.".buttonSetCaption('".addslashes($caption)."');\n";
17311                         $this->javascript .= 'f'.$name.".setAction('MouseUp','".addslashes($action)."');\n";
17312                         $this->javascript .= 'f'.$name.".highlight='push';\n";
17313                         $this->javascript .= 'f'.$name.".print=false;\n";
17314                         return;
17315                 }
17316                 // get default style
17317                 $prop = array_merge($this->getFormDefaultProp(), $prop);
17318                 $prop['Pushbutton'] = 'true';
17319                 $prop['highlight'] = 'push';
17320                 $prop['display'] = 'display.noPrint';
17321                 // get annotation data
17322                 $popt = $this->getAnnotOptFromJSProp($prop);
17323                 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
17324                 $fontstyle = sprintf('/F%d %.2F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
17325                 $popt['da'] = $fontstyle;
17326                 // build appearance stream
17327                 $popt['ap'] = array();
17328                 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
17329                 $tmpid = $this->startTemplate($w, $h, false);
17330                 $bw = (2 / $this->k); // border width
17331                 $border = array(
17332                         'L' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)),
17333                         'R' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)),
17334                         'T' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)),
17335                         'B' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)));
17336                 $this->SetFillColor(204);
17337                 $this->Cell($w, $h, $caption, $border, 0, 'C', true, '', 1, false, 'T', 'M');
17338                 $this->endTemplate();
17339                 --$this->n;
17340                 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
17341                 unset($this->xobjects[$tmpid]);
17342                 $popt['ap']['n'] .= 'Q EMC';
17343                 // set additional default options
17344                 if (!isset($popt['mk'])) {
17345                         $popt['mk'] = array();
17346                 }
17347                 $ann_obj_id = ($this->n + 1);
17348                 if (!empty($action) AND !is_array($action)) {
17349                         $ann_obj_id = ($this->n + 2);
17350                 }
17351                 $popt['mk']['ca'] = $this->_textstring($caption, $ann_obj_id);
17352                 $popt['mk']['rc'] = $this->_textstring($caption, $ann_obj_id);
17353                 $popt['mk']['ac'] = $this->_textstring($caption, $ann_obj_id);
17354                 // merge options
17355                 $opt = array_merge($popt, $opt);
17356                 // set remaining annotation data
17357                 $opt['Subtype'] = 'Widget';
17358                 $opt['ft'] = 'Btn';
17359                 $opt['t'] = $caption;
17360                 $opt['v'] = $name;
17361                 if (!empty($action)) {
17362                         if (is_array($action)) {
17363                                 // form action options as on section 12.7.5 of PDF32000_2008.
17364                                 $opt['aa'] = '/D <<';
17365                                 $bmode = array('SubmitForm', 'ResetForm', 'ImportData');
17366                                 foreach ($action AS $key => $val) {
17367                                         if (($key == 'S') AND in_array($val, $bmode)) {
17368                                                 $opt['aa'] .= ' /S /'.$val;
17369                                         } elseif (($key == 'F') AND (!empty($val))) {
17370                                                 $opt['aa'] .= ' /F '.$this->_datastring($val, $ann_obj_id);
17371                                         } elseif (($key == 'Fields') AND is_array($val) AND !empty($val)) {
17372                                                 $opt['aa'] .= ' /Fields [';
17373                                                 foreach ($val AS $field) {
17374                                                         $opt['aa'] .= ' '.$this->_textstring($field, $ann_obj_id);
17375                                                 }
17376                                                 $opt['aa'] .= ']';
17377                                         } elseif (($key == 'Flags')) {
17378                                                 $ff = 0;
17379                                                 if (is_array($val)) {
17380                                                         foreach ($val AS $flag) {
17381                                                                 switch ($flag) {
17382                                                                         case 'Include/Exclude': {
17383                                                                                 $ff += 1 << 0;
17384                                                                                 break;
17385                                                                         }
17386                                                                         case 'IncludeNoValueFields': {
17387                                                                                 $ff += 1 << 1;
17388                                                                                 break;
17389                                                                         }
17390                                                                         case 'ExportFormat': {
17391                                                                                 $ff += 1 << 2;
17392                                                                                 break;
17393                                                                         }
17394                                                                         case 'GetMethod': {
17395                                                                                 $ff += 1 << 3;
17396                                                                                 break;
17397                                                                         }
17398                                                                         case 'SubmitCoordinates': {
17399                                                                                 $ff += 1 << 4;
17400                                                                                 break;
17401                                                                         }
17402                                                                         case 'XFDF': {
17403                                                                                 $ff += 1 << 5;
17404                                                                                 break;
17405                                                                         }
17406                                                                         case 'IncludeAppendSaves': {
17407                                                                                 $ff += 1 << 6;
17408                                                                                 break;
17409                                                                         }
17410                                                                         case 'IncludeAnnotations': {
17411                                                                                 $ff += 1 << 7;
17412                                                                                 break;
17413                                                                         }
17414                                                                         case 'SubmitPDF': {
17415                                                                                 $ff += 1 << 8;
17416                                                                                 break;
17417                                                                         }
17418                                                                         case 'CanonicalFormat': {
17419                                                                                 $ff += 1 << 9;
17420                                                                                 break;
17421                                                                         }
17422                                                                         case 'ExclNonUserAnnots': {
17423                                                                                 $ff += 1 << 10;
17424                                                                                 break;
17425                                                                         }
17426                                                                         case 'ExclFKey': {
17427                                                                                 $ff += 1 << 11;
17428                                                                                 break;
17429                                                                         }
17430                                                                         case 'EmbedForm': {
17431                                                                                 $ff += 1 << 13;
17432                                                                                 break;
17433                                                                         }
17434                                                                 }
17435                                                         }
17436                                                 } else {
17437                                                         $ff = intval($val);
17438                                                 }
17439                                                 $opt['aa'] .= ' /Flags '.$ff;
17440                                         }
17441                                 }
17442                                 $opt['aa'] .= ' >>';
17443                         } else {
17444                                 // Javascript action or raw action command
17445                                 $js_obj_id = $this->addJavascriptObject($action);
17446                                 $opt['aa'] = '/D '.$js_obj_id.' 0 R';
17447                         }
17448                 }
17449                 $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
17450                 if ($this->rtl) {
17451                         $this->x -= $w;
17452                 } else {
17453                         $this->x += $w;
17454                 }
17455         }
17456 
17457         // --- END FORMS FIELDS ------------------------------------------------
17458 
17466         protected function _putsignature() {
17467                 if ((!$this->sign) OR (!isset($this->signature_data['cert_type']))) {
17468                         return;
17469                 }
17470                 $sigobjid = ($this->sig_obj_id + 1);
17471                 $out = $this->_getobj($sigobjid)."\n";
17472                 $out .= '<< /Type /Sig';
17473                 $out .= ' /Filter /Adobe.PPKLite';
17474                 $out .= ' /SubFilter /adbe.pkcs7.detached';
17475                 $out .= ' '.$this->byterange_string;
17476                 $out .= ' /Contents<'.str_repeat('0', $this->signature_max_length).'>';
17477                 $out .= ' /Reference ['; // array of signature reference dictionaries
17478                 $out .= ' << /Type /SigRef';
17479                 if ($this->signature_data['cert_type'] > 0) {
17480                         $out .= ' /TransformMethod /DocMDP';
17481                         $out .= ' /TransformParams <<';
17482                         $out .= ' /Type /TransformParams';
17483                         $out .= ' /P '.$this->signature_data['cert_type'];
17484                         $out .= ' /V /1.2';
17485                 } else {
17486                         $out .= ' /TransformMethod /UR3';
17487                         $out .= ' /TransformParams <<';
17488                         $out .= ' /Type /TransformParams';
17489                         $out .= ' /V /2.2';
17490                         if (!$this->empty_string($this->ur['document'])) {
17491                                 $out .= ' /Document['.$this->ur['document'].']';
17492                         }
17493                         if (!$this->empty_string($this->ur['form'])) {
17494                                 $out .= ' /Form['.$this->ur['form'].']';
17495                         }
17496                         if (!$this->empty_string($this->ur['signature'])) {
17497                                 $out .= ' /Signature['.$this->ur['signature'].']';
17498                         }
17499                         if (!$this->empty_string($this->ur['annots'])) {
17500                                 $out .= ' /Annots['.$this->ur['annots'].']';
17501                         }
17502                         if (!$this->empty_string($this->ur['ef'])) {
17503                                 $out .= ' /EF['.$this->ur['ef'].']';
17504                         }
17505                         if (!$this->empty_string($this->ur['formex'])) {
17506                                 $out .= ' /FormEX['.$this->ur['formex'].']';
17507                         }
17508                 }
17509                 $out .= ' >>'; // close TransformParams
17510                 // optional digest data (values must be calculated and replaced later)
17511                 //$out .= ' /Data ********** 0 R';
17512                 //$out .= ' /DigestMethod/MD5';
17513                 //$out .= ' /DigestLocation[********** 34]';
17514                 //$out .= ' /DigestValue<********************************>';
17515                 $out .= ' >>';
17516                 $out .= ' ]'; // end of reference
17517                 if (isset($this->signature_data['info']['Name']) AND !$this->empty_string($this->signature_data['info']['Name'])) {
17518                         $out .= ' /Name '.$this->_textstring($this->signature_data['info']['Name'], $sigobjid);
17519                 }
17520                 if (isset($this->signature_data['info']['Location']) AND !$this->empty_string($this->signature_data['info']['Location'])) {
17521                         $out .= ' /Location '.$this->_textstring($this->signature_data['info']['Location'], $sigobjid);
17522                 }
17523                 if (isset($this->signature_data['info']['Reason']) AND !$this->empty_string($this->signature_data['info']['Reason'])) {
17524                         $out .= ' /Reason '.$this->_textstring($this->signature_data['info']['Reason'], $sigobjid);
17525                 }
17526                 if (isset($this->signature_data['info']['ContactInfo']) AND !$this->empty_string($this->signature_data['info']['ContactInfo'])) {
17527                         $out .= ' /ContactInfo '.$this->_textstring($this->signature_data['info']['ContactInfo'], $sigobjid);
17528                 }
17529                 $out .= ' /M '.$this->_datestring($sigobjid);
17530                 $out .= ' >>';
17531                 $out .= "\n".'endobj';
17532                 $this->_out($out);
17533         }
17534 
17552         public function setUserRights(
17553                         $enable=true,
17554                         $document='/FullSave',
17555                         $annots='/Create/Delete/Modify/Copy/Import/Export',
17556                         $form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate',
17557                         $signature='/Modify',
17558                         $ef='/Create/Delete/Modify/Import',
17559                         $formex='') {
17560                 $this->ur['enabled'] = $enable;
17561                 $this->ur['document'] = $document;
17562                 $this->ur['annots'] = $annots;
17563                 $this->ur['form'] = $form;
17564                 $this->ur['signature'] = $signature;
17565                 $this->ur['ef'] = $ef;
17566                 $this->ur['formex'] = $formex;
17567                 if (!$this->sign) {
17568                         $this->setSignature('', '', '', '', 0, array());
17569                 }
17570         }
17571 
17588         public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array()) {
17589                 // to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt
17590                 // to export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12
17591                 // to convert pfx certificate to pem: openssl
17592                 //     OpenSSL> pkcs12 -in <cert.pfx> -out <cert.crt> -nodes
17593                 $this->sign = true;
17594                 ++$this->n;
17595                 $this->sig_obj_id = $this->n; // signature widget
17596                 ++$this->n; // signature object ($this->sig_obj_id + 1)
17597                 $this->signature_data = array();
17598                 if (strlen($signing_cert) == 0) {
17599                         $signing_cert = 'file://'.dirname(__FILE__).'/tcpdf.crt';
17600                         $private_key_password = 'tcpdfdemo';
17601                 }
17602                 if (strlen($private_key) == 0) {
17603                         $private_key = $signing_cert;
17604                 }
17605                 $this->signature_data['signcert'] = $signing_cert;
17606                 $this->signature_data['privkey'] = $private_key;
17607                 $this->signature_data['password'] = $private_key_password;
17608                 $this->signature_data['extracerts'] = $extracerts;
17609                 $this->signature_data['cert_type'] = $cert_type;
17610                 $this->signature_data['info'] = $info;
17611         }
17612 
17624         public function setSignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1) {
17625                 $this->signature_appearance = $this->getSignatureAppearanceArray($x, $y, $w, $h, $page);
17626         }
17627 
17639         public function addEmptySignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1) {
17640                 ++$this->n;
17641                 $this->empty_signature_appearance[] = array('objid' => $this->n) + $this->getSignatureAppearanceArray($x, $y, $w, $h, $page);
17642         }
17643 
17656         protected function getSignatureAppearanceArray($x=0, $y=0, $w=0, $h=0, $page=-1) {
17657                 $sigapp = array();
17658                 if (($page < 1) OR ($page > $this->numpages)) {
17659                         $sigapp['page'] = $this->page;
17660                 } else {
17661                         $sigapp['page'] = intval($page);
17662                 }
17663                 $a = $x * $this->k;
17664                 $b = $this->pagedim[($sigapp['page'])]['h'] - (($y + $h) * $this->k);
17665                 $c = $w * $this->k;
17666                 $d = $h * $this->k;
17667                 $sigapp['rect'] = sprintf('%.2F %.2F %.2F %.2F', $a, $b, ($a + $c), ($b + $d));
17668                 return $sigapp;
17669         }
17670 
17678         public function startPageGroup($page='') {
17679                 if (empty($page)) {
17680                         $page = $this->page + 1;
17681                 }
17682                 $this->newpagegroup[$page] = sizeof($this->newpagegroup) + 1;
17683         }
17684 
17692         public function AliasNbPages($s='') {}
17693 
17701         public function AliasNumPage($s='') {}
17702 
17709         public function setStartingPageNumber($num=1) {
17710                 $this->starting_page_number = max(0, intval($num));
17711         }
17712 
17720         public function getAliasRightShift() {
17721                 // calculate aproximatively the ratio between widths of aliases and replacements.
17722                 $ref = '{'.$this->alias_right_shift.'}{'.$this->alias_tot_pages.'}{'.$this->alias_num_page.'}';
17723                 $rep = str_repeat(' ', $this->GetNumChars($ref));
17724                 $wdiff = max(1, ($this->GetStringWidth($ref) / $this->GetStringWidth($rep)));
17725                 $sdiff = sprintf('%.3F', $wdiff);
17726                 $alias = $this->alias_right_shift.$sdiff.'}';
17727                 if ($this->isUnicodeFont()) {
17728                         $alias = '{'.$alias;
17729                 }
17730                 return $alias;
17731         }
17732 
17741         public function getAliasNbPages() {
17742                 if ($this->isUnicodeFont()) {
17743                         return '{'.$this->alias_tot_pages.'}';
17744                 }
17745                 return $this->alias_tot_pages;
17746         }
17747 
17756         public function getAliasNumPage() {
17757                 if ($this->isUnicodeFont()) {
17758                         return '{'.$this->alias_num_page.'}';
17759                 }
17760                 return $this->alias_num_page;
17761         }
17762 
17771         public function getPageGroupAlias() {
17772                 if ($this->isUnicodeFont()) {
17773                         return '{'.$this->alias_group_tot_pages.'}';
17774                 }
17775                 return $this->alias_group_tot_pages;
17776         }
17777 
17786         public function getPageNumGroupAlias() {
17787                 if ($this->isUnicodeFont()) {
17788                         return '{'.$this->alias_group_num_page.'}';
17789                 }
17790                 return $this->alias_group_num_page;
17791         }
17792 
17799         public function getGroupPageNo() {
17800                 return $this->pagegroups[$this->currpagegroup];
17801         }
17802 
17809         public function getGroupPageNoFormatted() {
17810                 return $this->formatPageNumber($this->getGroupPageNo());
17811         }
17812 
17820         protected function formatPageNumber($num) {
17821                 return number_format((float)$num, 0, '', '.');
17822         }
17823 
17832         protected function formatTOCPageNumber($num) {
17833                 return number_format((float)$num, 0, '', '.');
17834         }
17835 
17842         public function PageNoFormatted() {
17843                 return $this->formatPageNumber($this->PageNo());
17844         }
17845 
17851         protected function _putocg() {
17852                 if (empty($this->pdflayers)) {
17853                         return;
17854                 }
17855                 foreach ($this->pdflayers as $key => $layer) {
17856                          $this->pdflayers[$key]['objid'] = $this->_newobj();
17857                          $out = '<< /Type /OCG';
17858                          $out .= ' /Name '.$this->_textstring($layer['name'], $this->pdflayers[$key]['objid']);
17859                          $out .= ' /Usage <<';
17860                          $out .= ' /Print <</PrintState /'.($layer['print']?'ON':'OFF').'>>';
17861                          $out .= ' /View <</ViewState /'.($layer['view']?'ON':'OFF').'>>';
17862                          $out .= ' >> >>';
17863                          $out .= "\n".'endobj';
17864                          $this->_out($out);
17865                 }
17866         }
17867 
17876         public function startLayer($name='', $print=true, $view=true) {
17877                 $layer = sprintf('LYR%03d', (count($this->pdflayers) + 1));
17878                 if (empty($name)) {
17879                         $name = $layer;
17880                 } else {
17881                         $name = preg_replace('/[^a-zA-Z0-9_\-]/', '', $name);
17882                 }
17883                 $this->pdflayers[] = array('layer' => $layer, 'name' => $name, 'print' => $print, 'view' => $view);
17884                 $this->openMarkedContent = true;
17885                 $this->_out('/OC /'.$layer.' BDC');
17886         }
17887 
17893         public function endLayer() {
17894                 if ($this->openMarkedContent) {
17895                         // close existing open marked-content layer
17896                         $this->_out('EMC');
17897                         $this->openMarkedContent = false;
17898                 }
17899         }
17900 
17909         public function setVisibility($v) {
17910                 $this->endLayer();
17911                 switch($v) {
17912                         case 'print': {
17913                                 $this->startLayer('Print', true, false);
17914                                 break;
17915                         }
17916                         case 'view':
17917                         case 'screen': {
17918                                 $this->startLayer('View', false, true);
17919                                 break;
17920                         }
17921                         case 'all': {
17922                                 $this->_out('');
17923                                 break;
17924                         }
17925                         default: {
17926                                 $this->Error('Incorrect visibility: '.$v);
17927                                 break;
17928                         }
17929                 }
17930         }
17931 
17939         protected function addExtGState($parms) {
17940                 if ($this->pdfa_mode) {
17941                         // transparencies are not allowed in PDF/A mode
17942                         return;
17943                 }
17944                 // check if this ExtGState already exist
17945                 foreach ($this->extgstates as $i => $ext) {
17946                         if ($ext['parms'] == $parms) {
17947                                 if ($this->inxobj) {
17948                                         // we are inside an XObject template
17949                                         $this->xobjects[$this->xobjid]['extgstates'][$i] = $ext;
17950                                 }
17951                                 // return reference to existing ExtGState
17952                                 return $i;
17953                         }
17954                 }
17955                 $n = (count($this->extgstates) + 1);
17956                 $this->extgstates[$n] = array('parms' => $parms);
17957                 if ($this->inxobj) {
17958                         // we are inside an XObject template
17959                         $this->xobjects[$this->xobjid]['extgstates'][$n] = $this->extgstates[$n];
17960                 }
17961                 return $n;
17962         }
17963 
17970         protected function setExtGState($gs) {
17971                 if ($this->pdfa_mode) {
17972                         // transparency is not allowed in PDF/A mode
17973                         return;
17974                 }
17975                 $this->_out(sprintf('/GS%d gs', $gs));
17976         }
17977 
17983         protected function _putextgstates() {
17984                 if ($this->pdfa_mode) {
17985                         // transparencies are not allowed in PDF/A mode
17986                         return;
17987                 }
17988                 foreach ($this->extgstates as $i => $ext) {
17989                         $this->extgstates[$i]['n'] = $this->_newobj();
17990                         $out = '<< /Type /ExtGState';
17991                         foreach ($ext['parms'] as $k => $v) {
17992                                 if (is_float($v)) {
17993                                         $v = sprintf('%.2F', $v);
17994                                 }
17995                                 $out .= ' /'.$k.' '.$v;
17996                         }
17997                         $out .= ' >>';
17998                         $out .= "\n".'endobj';
17999                         $this->_out($out);
18000                 }
18001         }
18002 
18010         public function setAlpha($alpha, $bm='Normal') {
18011                 if ($this->pdfa_mode) {
18012                         // transparency is not allowed in PDF/A mode
18013                         return;
18014                 }
18015                 $gs = $this->addExtGState(array('ca' => $alpha, 'CA' => $alpha, 'BM' => '/'.$bm, 'AIS' => 'false'));
18016                 $this->setExtGState($gs);
18017         }
18018 
18025         public function setJPEGQuality($quality) {
18026                 if (($quality < 1) OR ($quality > 100)) {
18027                         $quality = 75;
18028                 }
18029                 $this->jpeg_quality = intval($quality);
18030         }
18031 
18038         public function setDefaultTableColumns($cols=4) {
18039                 $this->default_table_columns = intval($cols);
18040         }
18041 
18048         public function setCellHeightRatio($h) {
18049                 $this->cell_height_ratio = $h;
18050         }
18051 
18057         public function getCellHeightRatio() {
18058                 return $this->cell_height_ratio;
18059         }
18060 
18067         public function setPDFVersion($version='1.7') {
18068                 if ($this->pdfa_mode) {
18069                         // PDF/A mode
18070                         $this->PDFVersion = '1.4';
18071                 } else {
18072                         $this->PDFVersion = $version;
18073                 }
18074         }
18075 
18085         public function setViewerPreferences($preferences) {
18086                 $this->viewer_preferences = $preferences;
18087         }
18088 
18102         public function colorRegistrationBar($x, $y, $w, $h, $transition=true, $vertical=false, $colors='A,R,G,B,C,M,Y,K') {
18103                 $bars = explode(',', $colors);
18104                 $numbars = count($bars); // number of bars to print
18105                 // set bar measures
18106                 if ($vertical) {
18107                         $coords = array(0, 0, 0, 1);
18108                         $wb = $w / $numbars; // bar width
18109                         $hb = $h; // bar height
18110                         $xd = $wb; // delta x
18111                         $yd = 0; // delta y
18112                 } else {
18113                         $coords = array(1, 0, 0, 0);
18114                         $wb = $w; // bar width
18115                         $hb = $h / $numbars; // bar height
18116                         $xd = 0; // delta x
18117                         $yd = $hb; // delta y
18118                 }
18119                 $xb = $x;
18120                 $yb = $y;
18121                 foreach ($bars as $col) {
18122                         switch ($col) {
18123                                 // set transition colors
18124                                 case 'A': { // BLACK
18125                                         $col_a = array(255);
18126                                         $col_b = array(0);
18127                                         break;
18128                                 }
18129                                 case 'W': { // WHITE
18130                                         $col_a = array(0);
18131                                         $col_b = array(255);
18132                                         break;
18133                                 }
18134                                 case 'R': { // R
18135                                         $col_a = array(255,255,255);
18136                                         $col_b = array(255,0,0);
18137                                         break;
18138                                 }
18139                                 case 'G': { // G
18140                                         $col_a = array(255,255,255);
18141                                         $col_b = array(0,255,0);
18142                                         break;
18143                                 }
18144                                 case 'B': { // B
18145                                         $col_a = array(255,255,255);
18146                                         $col_b = array(0,0,255);
18147                                         break;
18148                                 }
18149                                 case 'C': { // C
18150                                         $col_a = array(0,0,0,0);
18151                                         $col_b = array(100,0,0,0);
18152                                         break;
18153                                 }
18154                                 case 'M': { // M
18155                                         $col_a = array(0,0,0,0);
18156                                         $col_b = array(0,100,0,0);
18157                                         break;
18158                                 }
18159                                 case 'Y': { // Y
18160                                         $col_a = array(0,0,0,0);
18161                                         $col_b = array(0,0,100,0);
18162                                         break;
18163                                 }
18164                                 case 'K': { // K
18165                                         $col_a = array(0,0,0,0);
18166                                         $col_b = array(0,0,0,100);
18167                                         break;
18168                                 }
18169                                 default: { // GRAY
18170                                         $col_a = array(255);
18171                                         $col_b = array(0);
18172                                         break;
18173                                 }
18174                         }
18175                         if ($transition) {
18176                                 // color gradient
18177                                 $this->LinearGradient($xb, $yb, $wb, $hb, $col_a, $col_b, $coords);
18178                         } else {
18179                                 // color rectangle
18180                                 $this->SetFillColorArray($col_b);
18181                                 $this->Rect($xb, $yb, $wb, $hb, 'F', array());
18182                         }
18183                         $xb += $xd;
18184                         $yb += $yd;
18185                 }
18186         }
18187 
18200         public function cropMark($x, $y, $w, $h, $type='A,B,C,D', $color=array(0,0,0)) {
18201                 $this->SetLineStyle(array('width' => (0.5 / $this->k), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $color));
18202                 $crops = explode(',', $type);
18203                 $numcrops = count($crops); // number of crop marks to print
18204                 $dw = $w / 4; // horizontal space to leave before the intersection point
18205                 $dh = $h / 4; // vertical space to leave before the intersection point
18206                 foreach ($crops as $crop) {
18207                         switch ($crop) {
18208                                 case 'A': {
18209                                         $x1 = $x;
18210                                         $y1 = $y - $h;
18211                                         $x2 = $x;
18212                                         $y2 = $y - $dh;
18213                                         $x3 = $x - $w;
18214                                         $y3 = $y;
18215                                         $x4 = $x - $dw;
18216                                         $y4 = $y;
18217                                         break;
18218                                 }
18219                                 case 'B': {
18220                                         $x1 = $x;
18221                                         $y1 = $y - $h;
18222                                         $x2 = $x;
18223                                         $y2 = $y - $dh;
18224                                         $x3 = $x + $dw;
18225                                         $y3 = $y;
18226                                         $x4 = $x + $w;
18227                                         $y4 = $y;
18228                                         break;
18229                                 }
18230                                 case 'C': {
18231                                         $x1 = $x - $w;
18232                                         $y1 = $y;
18233                                         $x2 = $x - $dw;
18234                                         $y2 = $y;
18235                                         $x3 = $x;
18236                                         $y3 = $y + $dh;
18237                                         $x4 = $x;
18238                                         $y4 = $y + $h;
18239                                         break;
18240                                 }
18241                                 case 'D': {
18242                                         $x1 = $x + $dw;
18243                                         $y1 = $y;
18244                                         $x2 = $x + $w;
18245                                         $y2 = $y;
18246                                         $x3 = $x;
18247                                         $y3 = $y + $dh;
18248                                         $x4 = $x;
18249                                         $y4 = $y + $h;
18250                                         break;
18251                                 }
18252                         }
18253                         $this->Line($x1, $y1, $x2, $y2);
18254                         $this->Line($x3, $y3, $x4, $y4);
18255                 }
18256         }
18257 
18270         public function registrationMark($x, $y, $r, $double=false, $cola=array(0,0,0), $colb=array(255,255,255)) {
18271                 $line_style = array('width' => (0.5 / $this->k), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $cola);
18272                 $this->SetFillColorArray($cola);
18273                 $this->PieSector($x, $y, $r, 90, 180, 'F');
18274                 $this->PieSector($x, $y, $r, 270, 360, 'F');
18275                 $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8);
18276                 if ($double) {
18277                         $r2 = $r * 0.5;
18278                         $this->SetFillColorArray($colb);
18279                         $this->PieSector($x, $y, $r2, 90, 180, 'F');
18280                         $this->PieSector($x, $y, $r2, 270, 360, 'F');
18281                         $this->SetFillColorArray($cola);
18282                         $this->PieSector($x, $y, $r2, 0, 90, 'F');
18283                         $this->PieSector($x, $y, $r2, 180, 270, 'F');
18284                         $this->Circle($x, $y, $r2, 0, 360, 'C', $line_style, array(), 8);
18285                 }
18286         }
18287 
18301         public function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) {
18302                 $this->Clip($x, $y, $w, $h);
18303                 $this->Gradient(2, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
18304         }
18305 
18319         public function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) {
18320                 $this->Clip($x, $y, $w, $h);
18321                 $this->Gradient(3, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
18322         }
18323 
18342         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) {
18343                 if ($this->pdfa_mode) {
18344                         return;
18345                 }
18346                 $this->Clip($x, $y, $w, $h);
18347                 $n = count($this->gradients) + 1;
18348                 $this->gradients[$n] = array();
18349                 $this->gradients[$n]['type'] = 6; //coons patch mesh
18350                 $this->gradients[$n]['coords'] = array();
18351                 $this->gradients[$n]['antialias'] = $antialias;
18352                 $this->gradients[$n]['colors'] = array();
18353                 $this->gradients[$n]['transparency'] = false;
18354                 //check the coords array if it is the simple array or the multi patch array
18355                 if (!isset($coords[0]['f'])) {
18356                         //simple array -> convert to multi patch array
18357                         if (!isset($col1[1])) {
18358                                 $col1[1] = $col1[2] = $col1[0];
18359                         }
18360                         if (!isset($col2[1])) {
18361                                 $col2[1] = $col2[2] = $col2[0];
18362                         }
18363                         if (!isset($col3[1])) {
18364                                 $col3[1] = $col3[2] = $col3[0];
18365                         }
18366                         if (!isset($col4[1])) {
18367                                 $col4[1] = $col4[2] = $col4[0];
18368                         }
18369                         $patch_array[0]['f'] = 0;
18370                         $patch_array[0]['points'] = $coords;
18371                         $patch_array[0]['colors'][0]['r'] = $col1[0];
18372                         $patch_array[0]['colors'][0]['g'] = $col1[1];
18373                         $patch_array[0]['colors'][0]['b'] = $col1[2];
18374                         $patch_array[0]['colors'][1]['r'] = $col2[0];
18375                         $patch_array[0]['colors'][1]['g'] = $col2[1];
18376                         $patch_array[0]['colors'][1]['b'] = $col2[2];
18377                         $patch_array[0]['colors'][2]['r'] = $col3[0];
18378                         $patch_array[0]['colors'][2]['g'] = $col3[1];
18379                         $patch_array[0]['colors'][2]['b'] = $col3[2];
18380                         $patch_array[0]['colors'][3]['r'] = $col4[0];
18381                         $patch_array[0]['colors'][3]['g'] = $col4[1];
18382                         $patch_array[0]['colors'][3]['b'] = $col4[2];
18383                 } else {
18384                         //multi patch array
18385                         $patch_array = $coords;
18386                 }
18387                 $bpcd = 65535; //16 bits per coordinate
18388                 //build the data stream
18389                 $this->gradients[$n]['stream'] = '';
18390                 $count_patch = count($patch_array);
18391                 for ($i=0; $i < $count_patch; ++$i) {
18392                         $this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit
18393                         $count_points = count($patch_array[$i]['points']);
18394                         for ($j=0; $j < $count_points; ++$j) {
18395                                 //each point as 16 bit
18396                                 $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j] - $coords_min) / ($coords_max - $coords_min)) * $bpcd;
18397                                 if ($patch_array[$i]['points'][$j] < 0) {
18398                                         $patch_array[$i]['points'][$j] = 0;
18399                                 }
18400                                 if ($patch_array[$i]['points'][$j] > $bpcd) {
18401                                         $patch_array[$i]['points'][$j] = $bpcd;
18402                                 }
18403                                 $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] / 256));
18404                                 $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] % 256));
18405                         }
18406                         $count_cols = count($patch_array[$i]['colors']);
18407                         for ($j=0; $j < $count_cols; ++$j) {
18408                                 //each color component as 8 bit
18409                                 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']);
18410                                 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']);
18411                                 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']);
18412                         }
18413                 }
18414                 //paint the gradient
18415                 $this->_out('/Sh'.$n.' sh');
18416                 //restore previous Graphic State
18417                 $this->_out('Q');
18418                 if ($this->inxobj) {
18419                         // we are inside an XObject template
18420                         $this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n];
18421                 }
18422         }
18423 
18434         protected function Clip($x, $y, $w, $h) {
18435                 if ($this->rtl) {
18436                         $x = $this->w - $x - $w;
18437                 }
18438                 //save current Graphic State
18439                 $s = 'q';
18440                 //set clipping area
18441                 $s .= sprintf(' %.2F %.2F %.2F %.2F re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k);
18442                 //set up transformation matrix for gradient
18443                 $s .= sprintf(' %.3F 0 0 %.3F %.3F %.3F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k);
18444                 $this->_out($s);
18445         }
18446 
18458         public function Gradient($type, $coords, $stops, $background=array(), $antialias=false) {
18459                 if ($this->pdfa_mode) {
18460                         return;
18461                 }
18462                 $n = count($this->gradients) + 1;
18463                 $this->gradients[$n] = array();
18464                 $this->gradients[$n]['type'] = $type;
18465                 $this->gradients[$n]['coords'] = $coords;
18466                 $this->gradients[$n]['antialias'] = $antialias;
18467                 $this->gradients[$n]['colors'] = array();
18468                 $this->gradients[$n]['transparency'] = false;
18469                 // color space
18470                 $numcolspace = count($stops[0]['color']);
18471                 $bcolor = array_values($background);
18472                 switch($numcolspace) {
18473                         case 4: { // CMYK
18474                                 $this->gradients[$n]['colspace'] = 'DeviceCMYK';
18475                                 if (!empty($background)) {
18476                                         $this->gradients[$n]['background'] = sprintf('%.3F %.3F %.3F %.3F', $bcolor[0]/100, $bcolor[1]/100, $bcolor[2]/100, $bcolor[3]/100);
18477                                 }
18478                                 break;
18479                         }
18480                         case 3: { // RGB
18481                                 $this->gradients[$n]['colspace'] = 'DeviceRGB';
18482                                 if (!empty($background)) {
18483                                         $this->gradients[$n]['background'] = sprintf('%.3F %.3F %.3F', $bcolor[0]/255, $bcolor[1]/255, $bcolor[2]/255);
18484                                 }
18485                                 break;
18486                         }
18487                         case 1: { // Gray scale
18488                                 $this->gradients[$n]['colspace'] = 'DeviceGray';
18489                                 if (!empty($background)) {
18490                                         $this->gradients[$n]['background'] = sprintf('%.3F', $bcolor[0]/255);
18491                                 }
18492                                 break;
18493                         }
18494                 }
18495                 $num_stops = count($stops);
18496                 $last_stop_id = $num_stops - 1;
18497                 foreach ($stops as $key => $stop) {
18498                         $this->gradients[$n]['colors'][$key] = array();
18499                         // offset represents a location along the gradient vector
18500                         if (isset($stop['offset'])) {
18501                                 $this->gradients[$n]['colors'][$key]['offset'] = $stop['offset'];
18502                         } else {
18503                                 if ($key == 0) {
18504                                         $this->gradients[$n]['colors'][$key]['offset'] = 0;
18505                                 } elseif ($key == $last_stop_id) {
18506                                         $this->gradients[$n]['colors'][$key]['offset'] = 1;
18507                                 } else {
18508                                         $offsetstep = (1 - $this->gradients[$n]['colors'][($key - 1)]['offset']) / ($num_stops - $key);
18509                                         $this->gradients[$n]['colors'][$key]['offset'] = $this->gradients[$n]['colors'][($key - 1)]['offset'] + $offsetstep;
18510                                 }
18511                         }
18512                         if (isset($stop['opacity'])) {
18513                                 $this->gradients[$n]['colors'][$key]['opacity'] = $stop['opacity'];
18514                                 if ((!$this->pdfa_mode) AND ($stop['opacity'] < 1)) {
18515                                         $this->gradients[$n]['transparency'] = true;
18516                                 }
18517                         } else {
18518                                 $this->gradients[$n]['colors'][$key]['opacity'] = 1;
18519                         }
18520                         // exponent for the exponential interpolation function
18521                         if (isset($stop['exponent'])) {
18522                                 $this->gradients[$n]['colors'][$key]['exponent'] = $stop['exponent'];
18523                         } else {
18524                                 $this->gradients[$n]['colors'][$key]['exponent'] = 1;
18525                         }
18526                         // set colors
18527                         $color = array_values($stop['color']);
18528                         switch($numcolspace) {
18529                                 case 4: { // CMYK
18530                                         $this->gradients[$n]['colors'][$key]['color'] = sprintf('%.3F %.3F %.3F %.3F', $color[0]/100, $color[1]/100, $color[2]/100, $color[3]/100);
18531                                         break;
18532                                 }
18533                                 case 3: { // RGB
18534                                         $this->gradients[$n]['colors'][$key]['color'] = sprintf('%.3F %.3F %.3F', $color[0]/255, $color[1]/255, $color[2]/255);
18535                                         break;
18536                                 }
18537                                 case 1: { // Gray scale
18538                                         $this->gradients[$n]['colors'][$key]['color'] = sprintf('%.3F', $color[0]/255);
18539                                         break;
18540                                 }
18541                         }
18542                 }
18543                 if ($this->gradients[$n]['transparency']) {
18544                         // paint luminosity gradient
18545                         $this->_out('/TGS'.$n.' gs');
18546                 }
18547                 //paint the gradient
18548                 $this->_out('/Sh'.$n.' sh');
18549                 //restore previous Graphic State
18550                 $this->_out('Q');
18551                 if ($this->inxobj) {
18552                         // we are inside an XObject template
18553                         $this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n];
18554                 }
18555         }
18556 
18563         function _putshaders() {
18564                 if ($this->pdfa_mode) {
18565                         return;
18566                 }
18567                 $idt = count($this->gradients); //index for transparency gradients
18568                 foreach ($this->gradients as $id => $grad) {
18569                         if (($grad['type'] == 2) OR ($grad['type'] == 3)) {
18570                                 $fc = $this->_newobj();
18571                                 $out = '<<';
18572                                 $out .= ' /FunctionType 3';
18573                                 $out .= ' /Domain [0 1]';
18574                                 $functions = '';
18575                                 $bounds = '';
18576                                 $encode = '';
18577                                 $i = 1;
18578                                 $num_cols = count($grad['colors']);
18579                                 $lastcols = $num_cols - 1;
18580                                 for ($i = 1; $i < $num_cols; ++$i) {
18581                                         $functions .= ($fc + $i).' 0 R ';
18582                                         if ($i < $lastcols) {
18583                                                 $bounds .= sprintf('%.3F ', $grad['colors'][$i]['offset']);
18584                                         }
18585                                         $encode .= '0 1 ';
18586                                 }
18587                                 $out .= ' /Functions ['.trim($functions).']';
18588                                 $out .= ' /Bounds ['.trim($bounds).']';
18589                                 $out .= ' /Encode ['.trim($encode).']';
18590                                 $out .= ' >>';
18591                                 $out .= "\n".'endobj';
18592                                 $this->_out($out);
18593                                 for ($i = 1; $i < $num_cols; ++$i) {
18594                                         $this->_newobj();
18595                                         $out = '<<';
18596                                         $out .= ' /FunctionType 2';
18597                                         $out .= ' /Domain [0 1]';
18598                                         $out .= ' /C0 ['.$grad['colors'][($i - 1)]['color'].']';
18599                                         $out .= ' /C1 ['.$grad['colors'][$i]['color'].']';
18600                                         $out .= ' /N '.$grad['colors'][$i]['exponent'];
18601                                         $out .= ' >>';
18602                                         $out .= "\n".'endobj';
18603                                         $this->_out($out);
18604                                 }
18605                                 // set transparency fuctions
18606                                 if ($grad['transparency']) {
18607                                         $ft = $this->_newobj();
18608                                         $out = '<<';
18609                                         $out .= ' /FunctionType 3';
18610                                         $out .= ' /Domain [0 1]';
18611                                         $functions = '';
18612                                         $i = 1;
18613                                         $num_cols = count($grad['colors']);
18614                                         for ($i = 1; $i < $num_cols; ++$i) {
18615                                                 $functions .= ($ft + $i).' 0 R ';
18616                                         }
18617                                         $out .= ' /Functions ['.trim($functions).']';
18618                                         $out .= ' /Bounds ['.trim($bounds).']';
18619                                         $out .= ' /Encode ['.trim($encode).']';
18620                                         $out .= ' >>';
18621                                         $out .= "\n".'endobj';
18622                                         $this->_out($out);
18623                                         for ($i = 1; $i < $num_cols; ++$i) {
18624                                                 $this->_newobj();
18625                                                 $out = '<<';
18626                                                 $out .= ' /FunctionType 2';
18627                                                 $out .= ' /Domain [0 1]';
18628                                                 $out .= ' /C0 ['.$grad['colors'][($i - 1)]['opacity'].']';
18629                                                 $out .= ' /C1 ['.$grad['colors'][$i]['opacity'].']';
18630                                                 $out .= ' /N '.$grad['colors'][$i]['exponent'];
18631                                                 $out .= ' >>';
18632                                                 $out .= "\n".'endobj';
18633                                                 $this->_out($out);
18634                                         }
18635                                 }
18636                         }
18637                         // set shading object
18638                         $this->_newobj();
18639                         $out = '<< /ShadingType '.$grad['type'];
18640                         if (isset($grad['colspace'])) {
18641                                 $out .= ' /ColorSpace /'.$grad['colspace'];
18642                         } else {
18643                                 $out .= ' /ColorSpace /DeviceRGB';
18644                         }
18645                         if (isset($grad['background']) AND !empty($grad['background'])) {
18646                                 $out .= ' /Background ['.$grad['background'].']';
18647                         }
18648                         if (isset($grad['antialias']) AND ($grad['antialias'] === true)) {
18649                                 $out .= ' /AntiAlias true';
18650                         }
18651                         if ($grad['type'] == 2) {
18652                                 $out .= ' '.sprintf('/Coords [%.3F %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]);
18653                                 $out .= ' /Domain [0 1]';
18654                                 $out .= ' /Function '.$fc.' 0 R';
18655                                 $out .= ' /Extend [true true]';
18656                                 $out .= ' >>';
18657                         } elseif ($grad['type'] == 3) {
18658                                 //x0, y0, r0, x1, y1, r1
18659                                 //at this this time radius of inner circle is 0
18660                                 $out .= ' '.sprintf('/Coords [%.3F %.3F 0 %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]);
18661                                 $out .= ' /Domain [0 1]';
18662                                 $out .= ' /Function '.$fc.' 0 R';
18663                                 $out .= ' /Extend [true true]';
18664                                 $out .= ' >>';
18665                         } elseif ($grad['type'] == 6) {
18666                                 $out .= ' /BitsPerCoordinate 16';
18667                                 $out .= ' /BitsPerComponent 8';
18668                                 $out .= ' /Decode[0 1 0 1 0 1 0 1 0 1]';
18669                                 $out .= ' /BitsPerFlag 8';
18670                                 $stream = $this->_getrawstream($grad['stream']);
18671                                 $out .= ' /Length '.strlen($stream);
18672                                 $out .= ' >>';
18673                                 $out .= ' stream'."\n".$stream."\n".'endstream';
18674                         }
18675                         $out .= "\n".'endobj';
18676                         $this->_out($out);
18677                         if ($grad['transparency']) {
18678                                 $shading_transparency = preg_replace('/\/ColorSpace \/[^\s]+/si', '/ColorSpace /DeviceGray', $out);
18679                                 $shading_transparency = preg_replace('/\/Function [0-9]+ /si', '/Function '.$ft.' ', $shading_transparency);
18680                         }
18681                         $this->gradients[$id]['id'] = $this->n;
18682                         // set pattern object
18683                         $this->_newobj();
18684                         $out = '<< /Type /Pattern /PatternType 2';
18685                         $out .= ' /Shading '.$this->gradients[$id]['id'].' 0 R';
18686                         $out .= ' >>';
18687                         $out .= "\n".'endobj';
18688                         $this->_out($out);
18689                         $this->gradients[$id]['pattern'] = $this->n;
18690                         // set shading and pattern for transparency mask
18691                         if ($grad['transparency']) {
18692                                 // luminosity pattern
18693                                 $idgs = $id + $idt;
18694                                 $this->_newobj();
18695                                 $this->_out($shading_transparency);
18696                                 $this->gradients[$idgs]['id'] = $this->n;
18697                                 $this->_newobj();
18698                                 $out = '<< /Type /Pattern /PatternType 2';
18699                                 $out .= ' /Shading '.$this->gradients[$idgs]['id'].' 0 R';
18700                                 $out .= ' >>';
18701                                 $out .= "\n".'endobj';
18702                                 $this->_out($out);
18703                                 $this->gradients[$idgs]['pattern'] = $this->n;
18704                                 // luminosity XObject
18705                                 $oid = $this->_newobj();
18706                                 $this->xobjects['LX'.$oid] = array('n' => $oid);
18707                                 $filter = '';
18708                                 $stream = 'q /a0 gs /Pattern cs /p'.$idgs.' scn 0 0 '.$this->wPt.' '.$this->hPt.' re f Q';
18709                                 if ($this->compress) {
18710                                         $filter = ' /Filter /FlateDecode';
18711                                         $stream = gzcompress($stream);
18712                                 }
18713                                 $stream = $this->_getrawstream($stream);
18714                                 $out = '<< /Type /XObject /Subtype /Form /FormType 1'.$filter;
18715                                 $out .= ' /Length '.strlen($stream);
18716                                 $rect = sprintf('%.2F %.2F', $this->wPt, $this->hPt);
18717                                 $out .= ' /BBox [0 0 '.$rect.']';
18718                                 $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceGray >>';
18719                                 $out .= ' /Resources <<';
18720                                 $out .= ' /ExtGState << /a0 << /ca 1 /CA 1 >> >>';
18721                                 $out .= ' /Pattern << /p'.$idgs.' '.$this->gradients[$idgs]['pattern'].' 0 R >>';
18722                                 $out .= ' >>';
18723                                 $out .= ' >> ';
18724                                 $out .= ' stream'."\n".$stream."\n".'endstream';
18725                                 $out .= "\n".'endobj';
18726                                 $this->_out($out);
18727                                 // SMask
18728                                 $this->_newobj();
18729                                 $out = '<< /Type /Mask /S /Luminosity /G '.($this->n - 1).' 0 R >>'."\n".'endobj';
18730                                 $this->_out($out);
18731                                 // ExtGState
18732                                 $this->_newobj();
18733                                 $out = '<< /Type /ExtGState /SMask '.($this->n - 1).' 0 R /AIS false >>'."\n".'endobj';
18734                                 $this->_out($out);
18735                                 $this->extgstates[] = array('n' => $this->n, 'name' => 'TGS'.$id);
18736                         }
18737                 }
18738         }
18739 
18755         public function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) {
18756                 $this->PieSectorXY($xc, $yc, $r, $r, $a, $b, $style, $cw, $o);
18757         }
18758 
18776         public function PieSectorXY($xc, $yc, $rx, $ry, $a, $b, $style='FD', $cw=false, $o=0, $nc=2) {
18777                 if ($this->rtl) {
18778                         $xc = $this->w - $xc;
18779                 }
18780                 $op = $this->getPathPaintOperator($style);
18781                 if ($op == 'f') {
18782                         $line_style = array();
18783                 }
18784                 if ($cw) {
18785                         $d = $b;
18786                         $b = 360 - $a + $o;
18787                         $a = 360 - $d + $o;
18788                 } else {
18789                         $b += $o;
18790                         $a += $o;
18791                 }
18792                 $this->_outellipticalarc($xc, $yc, $rx, $ry, 0, $a, $b, true, $nc);
18793                 $this->_out($op);
18794         }
18795 
18817         public function ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0, $fitonpage=false, $fixoutvals=false) {
18818                 if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) {
18819                         // convert EPS to raster image using GD or ImageMagick libraries
18820                         return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
18821                 }
18822                 if ($x === '') {
18823                         $x = $this->x;
18824                 }
18825                 if ($y === '') {
18826                         $y = $this->y;
18827                 }
18828                 // check page for no-write regions and adapt page margins if necessary
18829                 list($x, $y) = $this->checkPageRegions($h, $x, $y);
18830                 $k = $this->k;
18831                 if ($file{0} === '@') { // image from string
18832                         $data = substr($file, 1);
18833                 } else { // EPS/AI file
18834                         $data = file_get_contents($file);
18835                 }
18836                 if ($data === false) {
18837                         $this->Error('EPS file not found: '.$file);
18838                 }
18839                 $regs = array();
18840                 // EPS/AI compatibility check (only checks files created by Adobe Illustrator!)
18841                 preg_match("/%%Creator:([^\r\n]+)/", $data, $regs); # find Creator
18842                 if (count($regs) > 1) {
18843                         $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0"
18844                         if (strpos($version_str, 'Adobe Illustrator') !== false) {
18845                                 $versexp = explode(' ', $version_str);
18846                                 $version = (float)array_pop($versexp);
18847                                 if ($version >= 9) {
18848                                         $this->Error('This version of Adobe Illustrator file is not supported: '.$file);
18849                                 }
18850                         }
18851                 }
18852                 // strip binary bytes in front of PS-header
18853                 $start = strpos($data, '%!PS-Adobe');
18854                 if ($start > 0) {
18855                         $data = substr($data, $start);
18856                 }
18857                 // find BoundingBox params
18858                 preg_match("/%%BoundingBox:([^\r\n]+)/", $data, $regs);
18859                 if (count($regs) > 1) {
18860                         list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1]));
18861                 } else {
18862                         $this->Error('No BoundingBox found in EPS/AI file: '.$file);
18863                 }
18864                 $start = strpos($data, '%%EndSetup');
18865                 if ($start === false) {
18866                         $start = strpos($data, '%%EndProlog');
18867                 }
18868                 if ($start === false) {
18869                         $start = strpos($data, '%%BoundingBox');
18870                 }
18871                 $data = substr($data, $start);
18872                 $end = strpos($data, '%%PageTrailer');
18873                 if ($end===false) {
18874                         $end = strpos($data, 'showpage');
18875                 }
18876                 if ($end) {
18877                         $data = substr($data, 0, $end);
18878                 }
18879                 // calculate image width and height on document
18880                 if (($w <= 0) AND ($h <= 0)) {
18881                         $w = ($x2 - $x1) / $k;
18882                         $h = ($y2 - $y1) / $k;
18883                 } elseif ($w <= 0) {
18884                         $w = ($x2-$x1) / $k * ($h / (($y2 - $y1) / $k));
18885                 } elseif ($h <= 0) {
18886                         $h = ($y2 - $y1) / $k * ($w / (($x2 - $x1) / $k));
18887                 }
18888                 // fit the image on available space
18889                 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
18890                 if ($this->rasterize_vector_images) {
18891                         // convert EPS to raster image using GD or ImageMagick libraries
18892                         return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
18893                 }
18894                 // set scaling factors
18895                 $scale_x = $w / (($x2 - $x1) / $k);
18896                 $scale_y = $h / (($y2 - $y1) / $k);
18897                 // set alignment
18898                 $this->img_rb_y = $y + $h;
18899                 // set alignment
18900                 if ($this->rtl) {
18901                         if ($palign == 'L') {
18902                                 $ximg = $this->lMargin;
18903                         } elseif ($palign == 'C') {
18904                                 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
18905                         } elseif ($palign == 'R') {
18906                                 $ximg = $this->w - $this->rMargin - $w;
18907                         } else {
18908                                 $ximg = $x - $w;
18909                         }
18910                         $this->img_rb_x = $ximg;
18911                 } else {
18912                         if ($palign == 'L') {
18913                                 $ximg = $this->lMargin;
18914                         } elseif ($palign == 'C') {
18915                                 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
18916                         } elseif ($palign == 'R') {
18917                                 $ximg = $this->w - $this->rMargin - $w;
18918                         } else {
18919                                 $ximg = $x;
18920                         }
18921                         $this->img_rb_x = $ximg + $w;
18922                 }
18923                 if ($useBoundingBox) {
18924                         $dx = $ximg * $k - $x1;
18925                         $dy = $y * $k - $y1;
18926                 } else {
18927                         $dx = $ximg * $k;
18928                         $dy = $y * $k;
18929                 }
18930                 // save the current graphic state
18931                 $this->_out('q'.$this->epsmarker);
18932                 // translate
18933                 $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', 1, 0, 0, 1, $dx, $dy + ($this->hPt - (2 * $y * $k) - ($y2 - $y1))));
18934                 // scale
18935                 if (isset($scale_x)) {
18936                         $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $scale_x, 0, 0, $scale_y, $x1 * (1 - $scale_x), $y2 * (1 - $scale_y)));
18937                 }
18938                 // handle pc/unix/mac line endings
18939                 $lines = preg_split('/[\r\n]+/si', $data, -1, PREG_SPLIT_NO_EMPTY);
18940                 $u=0;
18941                 $cnt = count($lines);
18942                 for ($i=0; $i < $cnt; ++$i) {
18943                         $line = $lines[$i];
18944                         if (($line == '') OR ($line{0} == '%')) {
18945                                 continue;
18946                         }
18947                         $len = strlen($line);
18948                         // check for spot color names
18949                         $color_name = '';
18950                         if (strcasecmp('x', substr(trim($line), -1)) == 0) {
18951                                 if (preg_match('/\([^\)]*\)/', $line, $matches) > 0) {
18952                                         // extract spot color name
18953                                         $color_name = $matches[0];
18954                                         // remove color name from string
18955                                         $line = str_replace(' '.$color_name, '', $line);
18956                                         // remove pharentesis from color name
18957                                         $color_name = substr($color_name, 1, -1);
18958                                 }
18959                         }
18960                         $chunks = explode(' ', $line);
18961                         $cmd = trim(array_pop($chunks));
18962                         // RGB
18963                         if (($cmd == 'Xa') OR ($cmd == 'XA')) {
18964                                 $b = array_pop($chunks);
18965                                 $g = array_pop($chunks);
18966                                 $r = array_pop($chunks);
18967                                 $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!
18968                                 continue;
18969                         }
18970                         $skip = false;
18971                         if ($fixoutvals) {
18972                                 // check for values outside the bounding box
18973                                 switch ($cmd) {
18974                                         case 'm':
18975                                         case 'l':
18976                                         case 'L': {
18977                                                 // skip values outside bounding box
18978                                                 foreach ($chunks as $key => $val) {
18979                                                         if ((($key % 2) == 0) AND (($val < $x1) OR ($val > $x2))) {
18980                                                                 $skip = true;
18981                                                         } elseif ((($key % 2) != 0) AND (($val < $y1) OR ($val > $y2))) {
18982                                                                 $skip = true;
18983                                                         }
18984                                                 }
18985                                         }
18986                                 }
18987                         }
18988                         switch ($cmd) {
18989                                 case 'm':
18990                                 case 'l':
18991                                 case 'v':
18992                                 case 'y':
18993                                 case 'c':
18994                                 case 'k':
18995                                 case 'K':
18996                                 case 'g':
18997                                 case 'G':
18998                                 case 's':
18999                                 case 'S':
19000                                 case 'J':
19001                                 case 'j':
19002                                 case 'w':
19003                                 case 'M':
19004                                 case 'd':
19005                                 case 'n': {
19006                                         if ($skip) {
19007                                                 break;
19008                                         }
19009                                         $this->_out($line);
19010                                         break;
19011                                 }
19012                                 case 'x': {// custom fill color
19013                                         if (empty($color_name)) {
19014                                                 // CMYK color
19015                                                 list($col_c, $col_m, $col_y, $col_k) = $chunks;
19016                                                 $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' k');
19017                                         } else {
19018                                                 // Spot Color (CMYK + tint)
19019                                                 list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
19020                                                 $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
19021                                                 $color_cmd = sprintf('/CS%d cs %.3F scn', $this->spot_colors[$color_name]['i'], (1 - $col_t));
19022                                                 $this->_out($color_cmd);
19023                                         }
19024                                         break;
19025                                 }
19026                                 case 'X': { // custom stroke color
19027                                         if (empty($color_name)) {
19028                                                 // CMYK color
19029                                                 list($col_c, $col_m, $col_y, $col_k) = $chunks;
19030                                                 $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' K');
19031                                         } else {
19032                                                 // Spot Color (CMYK + tint)
19033                                                 list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
19034                                                 $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
19035                                                 $color_cmd = sprintf('/CS%d CS %.3F SCN', $this->spot_colors[$color_name]['i'], (1 - $col_t));
19036                                                 $this->_out($color_cmd);
19037                                         }
19038                                         break;
19039                                 }
19040                                 case 'Y':
19041                                 case 'N':
19042                                 case 'V':
19043                                 case 'L':
19044                                 case 'C': {
19045                                         if ($skip) {
19046                                                 break;
19047                                         }
19048                                         $line{$len-1} = strtolower($cmd);
19049                                         $this->_out($line);
19050                                         break;
19051                                 }
19052                                 case 'b':
19053                                 case 'B': {
19054                                         $this->_out($cmd . '*');
19055                                         break;
19056                                 }
19057                                 case 'f':
19058                                 case 'F': {
19059                                         if ($u > 0) {
19060                                                 $isU = false;
19061                                                 $max = min(($i + 5), $cnt);
19062                                                 for ($j = ($i + 1); $j < $max; ++$j) {
19063                                                         $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U')));
19064                                                 }
19065                                                 if ($isU) {
19066                                                         $this->_out('f*');
19067                                                 }
19068                                         } else {
19069                                                 $this->_out('f*');
19070                                         }
19071                                         break;
19072                                 }
19073                                 case '*u': {
19074                                         ++$u;
19075                                         break;
19076                                 }
19077                                 case '*U': {
19078                                         --$u;
19079                                         break;
19080                                 }
19081                         }
19082                 }
19083                 // restore previous graphic state
19084                 $this->_out($this->epsmarker.'Q');
19085                 if (!empty($border)) {
19086                         $bx = $this->x;
19087                         $by = $this->y;
19088                         $this->x = $ximg;
19089                         if ($this->rtl) {
19090                                 $this->x += $w;
19091                         }
19092                         $this->y = $y;
19093                         $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
19094                         $this->x = $bx;
19095                         $this->y = $by;
19096                 }
19097                 if ($link) {
19098                         $this->Link($ximg, $y, $w, $h, $link, 0);
19099                 }
19100                 // set pointer to align the next text/objects
19101                 switch($align) {
19102                         case 'T':{
19103                                 $this->y = $y;
19104                                 $this->x = $this->img_rb_x;
19105                                 break;
19106                         }
19107                         case 'M':{
19108                                 $this->y = $y + round($h/2);
19109                                 $this->x = $this->img_rb_x;
19110                                 break;
19111                         }
19112                         case 'B':{
19113                                 $this->y = $this->img_rb_y;
19114                                 $this->x = $this->img_rb_x;
19115                                 break;
19116                         }
19117                         case 'N':{
19118                                 $this->SetY($this->img_rb_y);
19119                                 break;
19120                         }
19121                         default:{
19122                                 break;
19123                         }
19124                 }
19125                 $this->endlinex = $this->img_rb_x;
19126         }
19127 
19133         public function setBarcode($bc='') {
19134                 $this->barcode = $bc;
19135         }
19136 
19143         public function getBarcode() {
19144                 return $this->barcode;
19145         }
19146 
19177         public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres='', $style='', $align='') {
19178                 if ($this->empty_string(trim($code))) {
19179                         return;
19180                 }
19181                 require_once(dirname(__FILE__).'/barcodes.php');
19182                 // save current graphic settings
19183                 $gvars = $this->getGraphicVars();
19184                 // create new barcode object
19185                 $barcodeobj = new TCPDFBarcode($code, $type);
19186                 $arrcode = $barcodeobj->getBarcodeArray();
19187                 if ($arrcode === false) {
19188                         $this->Error('Error in 1D barcode string');
19189                 }
19190                 // set default values
19191                 if (!isset($style['position'])) {
19192                         $style['position'] = '';
19193                 } elseif ($style['position'] == 'S') {
19194                         // keep this for backward compatibility
19195                         $style['position'] = '';
19196                         $style['stretch'] = true;
19197                 }
19198                 if (!isset($style['fitwidth'])) {
19199                         if (!isset($style['stretch'])) {
19200                                 $style['fitwidth'] = true;
19201                         } else {
19202                                 $style['fitwidth'] = false;
19203                         }
19204                 }
19205                 if ($style['fitwidth']) {
19206                         // disable stretch
19207                         $style['stretch'] = false;
19208                 }
19209                 if (!isset($style['stretch'])) {
19210                         if (($w === '') OR ($w <= 0)) {
19211                                 $style['stretch'] = false;
19212                         } else {
19213                                 $style['stretch'] = true;
19214                         }
19215                 }
19216                 if (!isset($style['fgcolor'])) {
19217                         $style['fgcolor'] = array(0,0,0); // default black
19218                 }
19219                 if (!isset($style['bgcolor'])) {
19220                         $style['bgcolor'] = false; // default transparent
19221                 }
19222                 if (!isset($style['border'])) {
19223                         $style['border'] = false;
19224                 }
19225                 $fontsize = 0;
19226                 if (!isset($style['text'])) {
19227                         $style['text'] = false;
19228                 }
19229                 if ($style['text'] AND isset($style['font'])) {
19230                         if (isset($style['fontsize'])) {
19231                                 $fontsize = $style['fontsize'];
19232                         }
19233                         $this->SetFont($style['font'], '', $fontsize);
19234                 }
19235                 if (!isset($style['stretchtext'])) {
19236                         $style['stretchtext'] = 4;
19237                 }
19238                 if ($x === '') {
19239                         $x = $this->x;
19240                 }
19241                 if ($y === '') {
19242                         $y = $this->y;
19243                 }
19244                 // check page for no-write regions and adapt page margins if necessary
19245                 list($x, $y) = $this->checkPageRegions($h, $x, $y);
19246                 if (($w === '') OR ($w <= 0)) {
19247                         if ($this->rtl) {
19248                                 $w = $x - $this->lMargin;
19249                         } else {
19250                                 $w = $this->w - $this->rMargin - $x;
19251                         }
19252                 }
19253                 // padding
19254                 if (!isset($style['padding'])) {
19255                         $padding = 0;
19256                 } elseif ($style['padding'] === 'auto') {
19257                         $padding = 10 * ($w / ($arrcode['maxw'] + 20));
19258                 } else {
19259                         $padding = floatval($style['padding']);
19260                 }
19261                 // horizontal padding
19262                 if (!isset($style['hpadding'])) {
19263                         $hpadding = $padding;
19264                 } elseif ($style['hpadding'] === 'auto') {
19265                         $hpadding = 10 * ($w / ($arrcode['maxw'] + 20));
19266                 } else {
19267                         $hpadding = floatval($style['hpadding']);
19268                 }
19269                 // vertical padding
19270                 if (!isset($style['vpadding'])) {
19271                         $vpadding = $padding;
19272                 } elseif ($style['vpadding'] === 'auto') {
19273                         $vpadding = ($hpadding / 2);
19274                 } else {
19275                         $vpadding = floatval($style['vpadding']);
19276                 }
19277                 // calculate xres (single bar width)
19278                 $max_xres = ($w - (2 * $hpadding)) / $arrcode['maxw'];
19279                 if ($style['stretch']) {
19280                         $xres = $max_xres;
19281                 } else {
19282                         if ($this->empty_string($xres)) {
19283                                 $xres = (0.141 * $this->k); // default bar width = 0.4 mm
19284                         }
19285                         if ($xres > $max_xres) {
19286                                 // correct xres to fit on $w
19287                                 $xres = $max_xres;
19288                         }
19289                         if ((isset($style['padding']) AND ($style['padding'] === 'auto'))
19290                                 OR (isset($style['hpadding']) AND ($style['hpadding'] === 'auto'))) {
19291                                 $hpadding = 10 * $xres;
19292                                 if (isset($style['vpadding']) AND ($style['vpadding'] === 'auto')) {
19293                                         $vpadding = ($hpadding / 2);
19294                                 }
19295                         }
19296                 }
19297                 if ($style['fitwidth']) {
19298                         $wold = $w;
19299                         $w = (($arrcode['maxw'] * $xres) + (2 * $hpadding));
19300                         if (isset($style['cellfitalign'])) {
19301                                 switch ($style['cellfitalign']) {
19302                                         case 'L': {
19303                                                 if ($this->rtl) {
19304                                                         $x -= ($wold - $w);
19305                                                 }
19306                                                 break;
19307                                         }
19308                                         case 'R': {
19309                                                 if (!$this->rtl) {
19310                                                         $x += ($wold - $w);
19311                                                 }
19312                                                 break;
19313                                         }
19314                                         case 'C': {
19315                                                 if ($this->rtl) {
19316                                                         $x -= (($wold - $w) / 2);
19317                                                 } else {
19318                                                         $x += (($wold - $w) / 2);
19319                                                 }
19320                                                 break;
19321                                         }
19322                                         default : {
19323                                                 break;
19324                                         }
19325                                 }
19326                         }
19327                 }
19328                 $text_height = ($this->cell_height_ratio * $fontsize / $this->k);
19329                 // height
19330                 if (($h === '') OR ($h <= 0)) {
19331                         // set default height
19332                         $h = (($arrcode['maxw'] * $xres) / 3) + (2 * $vpadding) + $text_height;
19333                 }
19334                 $barh = $h - $text_height - (2 * $vpadding);
19335                 if ($barh <=0) {
19336                         // try to reduce font or padding to fit barcode on available height
19337                         if ($text_height > $h) {
19338                                 $fontsize = (($h * $this->k) / (4 * $this->cell_height_ratio));
19339                                 $text_height = ($this->cell_height_ratio * $fontsize / $this->k);
19340                                 $this->SetFont($style['font'], '', $fontsize);
19341                         }
19342                         if ($vpadding > 0) {
19343                                 $vpadding = (($h - $text_height) / 4);
19344                         }
19345                         $barh = $h - $text_height - (2 * $vpadding);
19346                 }
19347                 // fit the barcode on available space
19348                 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false);
19349                 // set alignment
19350                 $this->img_rb_y = $y + $h;
19351                 // set alignment
19352                 if ($this->rtl) {
19353                         if ($style['position'] == 'L') {
19354                                 $xpos = $this->lMargin;
19355                         } elseif ($style['position'] == 'C') {
19356                                 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
19357                         } elseif ($style['position'] == 'R') {
19358                                 $xpos = $this->w - $this->rMargin - $w;
19359                         } else {
19360                                 $xpos = $x - $w;
19361                         }
19362                         $this->img_rb_x = $xpos;
19363                 } else {
19364                         if ($style['position'] == 'L') {
19365                                 $xpos = $this->lMargin;
19366                         } elseif ($style['position'] == 'C') {
19367                                 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
19368                         } elseif ($style['position'] == 'R') {
19369                                 $xpos = $this->w - $this->rMargin - $w;
19370                         } else {
19371                                 $xpos = $x;
19372                         }
19373                         $this->img_rb_x = $xpos + $w;
19374                 }
19375                 $xpos_rect = $xpos;
19376                 if (!isset($style['align'])) {
19377                         $style['align'] = 'C';
19378                 }
19379                 switch ($style['align']) {
19380                         case 'L': {
19381                                 $xpos = $xpos_rect + $hpadding;
19382                                 break;
19383                         }
19384                         case 'R': {
19385                                 $xpos = $xpos_rect + ($w - ($arrcode['maxw'] * $xres)) - $hpadding;
19386                                 break;
19387                         }
19388                         case 'C':
19389                         default : {
19390                                 $xpos = $xpos_rect + (($w - ($arrcode['maxw'] * $xres)) / 2);
19391                                 break;
19392                         }
19393                 }
19394                 $xpos_text = $xpos;
19395                 // barcode is always printed in LTR direction
19396                 $tempRTL = $this->rtl;
19397                 $this->rtl = false;
19398                 // print background color
19399                 if ($style['bgcolor']) {
19400                         $this->Rect($xpos_rect, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
19401                 } elseif ($style['border']) {
19402                         $this->Rect($xpos_rect, $y, $w, $h, 'D');
19403                 }
19404                 // set foreground color
19405                 $this->SetDrawColorArray($style['fgcolor']);
19406                 $this->SetTextColorArray($style['fgcolor']);
19407                 // print bars
19408                 foreach ($arrcode['bcode'] as $k => $v) {
19409                         $bw = ($v['w'] * $xres);
19410                         if ($v['t']) {
19411                                 // draw a vertical bar
19412                                 $ypos = $y + $vpadding + ($v['p'] * $barh / $arrcode['maxh']);
19413                                 $this->Rect($xpos, $ypos, $bw, ($v['h'] * $barh / $arrcode['maxh']), 'F', array(), $style['fgcolor']);
19414                         }
19415                         $xpos += $bw;
19416                 }
19417                 // print text
19418                 if ($style['text']) {
19419                         if (isset($style['label']) AND !$this->empty_string($style['label'])) {
19420                                 $label = $style['label'];
19421                         } else {
19422                                 $label = $code;
19423                         }
19424                         $txtwidth = ($arrcode['maxw'] * $xres);
19425                         if ($this->GetStringWidth($label) > $txtwidth) {
19426                                 $style['stretchtext'] = 2;
19427                         }
19428                         // print text
19429                         $this->x = $xpos_text;
19430                         $this->y = $y + $vpadding + $barh;
19431                         $cellpadding = $this->cell_padding;
19432                         $this->SetCellPadding(0);
19433                         $this->Cell($txtwidth, '', $label, 0, 0, 'C', false, '', $style['stretchtext'], false, 'T', 'T');
19434                         $this->cell_padding = $cellpadding;
19435                 }
19436                 // restore original direction
19437                 $this->rtl = $tempRTL;
19438                 // restore previous settings
19439                 $this->setGraphicVars($gvars);
19440                 // set pointer to align the next text/objects
19441                 switch($align) {
19442                         case 'T':{
19443                                 $this->y = $y;
19444                                 $this->x = $this->img_rb_x;
19445                                 break;
19446                         }
19447                         case 'M':{
19448                                 $this->y = $y + round($h / 2);
19449                                 $this->x = $this->img_rb_x;
19450                                 break;
19451                         }
19452                         case 'B':{
19453                                 $this->y = $this->img_rb_y;
19454                                 $this->x = $this->img_rb_x;
19455                                 break;
19456                         }
19457                         case 'N':{
19458                                 $this->SetY($this->img_rb_y);
19459                                 break;
19460                         }
19461                         default:{
19462                                 break;
19463                         }
19464                 }
19465                 $this->endlinex = $this->img_rb_x;
19466         }
19467 
19483         public function writeBarcode($x, $y, $w, $h, $type, $style, $font, $xres, $code) {
19484                 // convert old settings for the new write1DBarcode() function.
19485                 $xres = 1 / $xres;
19486                 $newstyle = array(
19487                         'position' => '',
19488                         'align' => '',
19489                         'stretch' => false,
19490                         'fitwidth' => false,
19491                         'cellfitalign' => '',
19492                         'border' => false,
19493                         'padding' => 0,
19494                         'fgcolor' => array(0,0,0),
19495                         'bgcolor' => false,
19496                         'text' => true,
19497                         'font' => $font,
19498                         'fontsize' => 8,
19499                         'stretchtext' => 4
19500                 );
19501                 if ($style & 1) {
19502                         $newstyle['border'] = true;
19503                 }
19504                 if ($style & 2) {
19505                         $newstyle['bgcolor'] = false;
19506                 }
19507                 if ($style & 4) {
19508                         $newstyle['position'] = 'C';
19509                 } elseif ($style & 8) {
19510                         $newstyle['position'] = 'L';
19511                 } elseif ($style & 16) {
19512                         $newstyle['position'] = 'R';
19513                 }
19514                 if ($style & 128) {
19515                         $newstyle['text'] = true;
19516                 }
19517                 if ($style & 256) {
19518                         $newstyle['stretchtext'] = 4;
19519                 }
19520                 $this->write1DBarcode($code, $type, $x, $y, $w, $h, $xres, $newstyle, '');
19521         }
19522 
19548         public function write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='', $distort=false) {
19549                 if ($this->empty_string(trim($code))) {
19550                         return;
19551                 }
19552                 require_once(dirname(__FILE__).'/2dbarcodes.php');
19553                 // save current graphic settings
19554                 $gvars = $this->getGraphicVars();
19555                 // create new barcode object
19556                 $barcodeobj = new TCPDF2DBarcode($code, $type);
19557                 $arrcode = $barcodeobj->getBarcodeArray();
19558                 if (($arrcode === false) OR empty($arrcode)) {
19559                         $this->Error('Error in 2D barcode string');
19560                 }
19561                 // set default values
19562                 if (!isset($style['position'])) {
19563                         $style['position'] = '';
19564                 }
19565                 if (!isset($style['fgcolor'])) {
19566                         $style['fgcolor'] = array(0,0,0); // default black
19567                 }
19568                 if (!isset($style['bgcolor'])) {
19569                         $style['bgcolor'] = false; // default transparent
19570                 }
19571                 if (!isset($style['border'])) {
19572                         $style['border'] = false;
19573                 }
19574                 // padding
19575                 if (!isset($style['padding'])) {
19576                         $style['padding'] = 0;
19577                 } elseif ($style['padding'] === 'auto') {
19578                         $style['padding'] = 4;
19579                 }
19580                 if (!isset($style['hpadding'])) {
19581                         $style['hpadding'] = $style['padding'];
19582                 } elseif ($style['hpadding'] === 'auto') {
19583                         $style['hpadding'] = 4;
19584                 }
19585                 if (!isset($style['vpadding'])) {
19586                         $style['vpadding'] = $style['padding'];
19587                 } elseif ($style['vpadding'] === 'auto') {
19588                         $style['vpadding'] = 4;
19589                 }
19590                 $hpad = (2 * $style['hpadding']);
19591                 $vpad = (2 * $style['vpadding']);
19592                 // cell (module) dimension
19593                 if (!isset($style['module_width'])) {
19594                         $style['module_width'] = 1; // width of a single module in points
19595                 }
19596                 if (!isset($style['module_height'])) {
19597                         $style['module_height'] = 1; // height of a single module in points
19598                 }
19599                 if ($x === '') {
19600                         $x = $this->x;
19601                 }
19602                 if ($y === '') {
19603                         $y = $this->y;
19604                 }
19605                 // check page for no-write regions and adapt page margins if necessary
19606                 list($x, $y) = $this->checkPageRegions($h, $x, $y);
19607                 // number of barcode columns and rows
19608                 $rows = $arrcode['num_rows'];
19609                 $cols = $arrcode['num_cols'];
19610                 // module width and height
19611                 $mw = $style['module_width'];
19612                 $mh = $style['module_height'];
19613                 // get max dimensions
19614                 if ($this->rtl) {
19615                         $maxw = $x - $this->lMargin;
19616                 } else {
19617                         $maxw = $this->w - $this->rMargin - $x;
19618                 }
19619                 $maxh = ($this->h - $this->tMargin - $this->bMargin);
19620                 $ratioHW = ((($rows * $mh) + $hpad) / (($cols * $mw) + $vpad));
19621                 $ratioWH = ((($cols * $mw) + $vpad) / (($rows * $mh) + $hpad));
19622                 if (!$distort) {
19623                         if (($maxw * $ratioHW) > $maxh) {
19624                                 $maxw = $maxh * $ratioWH;
19625                         }
19626                         if (($maxh * $ratioWH) > $maxw) {
19627                                 $maxh = $maxw * $ratioHW;
19628                         }
19629                 }
19630                 // set maximum dimesions
19631                 if ($w > $maxw) {
19632                         $w = $maxw;
19633                 }
19634                 if ($h > $maxh) {
19635                         $h = $maxh;
19636                 }
19637                 // set dimensions
19638                 if ((($w === '') OR ($w <= 0)) AND (($h === '') OR ($h <= 0))) {
19639                         $w = ($cols + $hpad) * ($mw / $this->k);
19640                         $h = ($rows + $vpad) * ($mh / $this->k);
19641                 } elseif (($w === '') OR ($w <= 0)) {
19642                         $w = $h * $ratioWH;
19643                 } elseif (($h === '') OR ($h <= 0)) {
19644                         $h = $w * $ratioHW;
19645                 }
19646                 // barcode size (excluding padding)
19647                 $bw = ($w * $cols) / ($cols + $hpad);
19648                 $bh = ($h * $rows) / ($rows + $vpad);
19649                 // dimension of single barcode cell unit
19650                 $cw = $bw / $cols;
19651                 $ch = $bh / $rows;
19652                 if (!$distort) {
19653                         if (($cw / $ch) > ($mw / $mh)) {
19654                                 // correct horizontal distortion
19655                                 $cw = $ch * $mw / $mh;
19656                                 $bw = $cw * $cols;
19657                                 $style['hpadding'] = ($w - $bw) / (2 * $cw);
19658                         } else {
19659                                 // correct vertical distortion
19660                                 $ch = $cw * $mh / $mw;
19661                                 $bh = $ch * $rows;
19662                                 $style['vpadding'] = ($h - $bh) / (2 * $ch);
19663                         }
19664                 }
19665                 // fit the barcode on available space
19666                 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false);
19667                 // set alignment
19668                 $this->img_rb_y = $y + $h;
19669                 // set alignment
19670                 if ($this->rtl) {
19671                         if ($style['position'] == 'L') {
19672                                 $xpos = $this->lMargin;
19673                         } elseif ($style['position'] == 'C') {
19674                                 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
19675                         } elseif ($style['position'] == 'R') {
19676                                 $xpos = $this->w - $this->rMargin - $w;
19677                         } else {
19678                                 $xpos = $x - $w;
19679                         }
19680                         $this->img_rb_x = $xpos;
19681                 } else {
19682                         if ($style['position'] == 'L') {
19683                                 $xpos = $this->lMargin;
19684                         } elseif ($style['position'] == 'C') {
19685                                 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
19686                         } elseif ($style['position'] == 'R') {
19687                                 $xpos = $this->w - $this->rMargin - $w;
19688                         } else {
19689                                 $xpos = $x;
19690                         }
19691                         $this->img_rb_x = $xpos + $w;
19692                 }
19693                 $xstart = $xpos + ($style['hpadding'] * $cw);
19694                 $ystart = $y + ($style['vpadding'] * $ch);
19695                 // barcode is always printed in LTR direction
19696                 $tempRTL = $this->rtl;
19697                 $this->rtl = false;
19698                 // print background color
19699                 if ($style['bgcolor']) {
19700                         $this->Rect($xpos, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
19701                 } elseif ($style['border']) {
19702                         $this->Rect($xpos, $y, $w, $h, 'D');
19703                 }
19704                 // set foreground color
19705                 $this->SetDrawColorArray($style['fgcolor']);
19706                 // print barcode cells
19707                 // for each row
19708                 for ($r = 0; $r < $rows; ++$r) {
19709                         $xr = $xstart;
19710                         // for each column
19711                         for ($c = 0; $c < $cols; ++$c) {
19712                                 if ($arrcode['bcode'][$r][$c] == 1) {
19713                                         // draw a single barcode cell
19714                                         $this->Rect($xr, $ystart, $cw, $ch, 'F', array(), $style['fgcolor']);
19715                                 }
19716                                 $xr += $cw;
19717                         }
19718                         $ystart += $ch;
19719                 }
19720                 // restore original direction
19721                 $this->rtl = $tempRTL;
19722                 // restore previous settings
19723                 $this->setGraphicVars($gvars);
19724                 // set pointer to align the next text/objects
19725                 switch($align) {
19726                         case 'T':{
19727                                 $this->y = $y;
19728                                 $this->x = $this->img_rb_x;
19729                                 break;
19730                         }
19731                         case 'M':{
19732                                 $this->y = $y + round($h/2);
19733                                 $this->x = $this->img_rb_x;
19734                                 break;
19735                         }
19736                         case 'B':{
19737                                 $this->y = $this->img_rb_y;
19738                                 $this->x = $this->img_rb_x;
19739                                 break;
19740                         }
19741                         case 'N':{
19742                                 $this->SetY($this->img_rb_y);
19743                                 break;
19744                         }
19745                         default:{
19746                                 break;
19747                         }
19748                 }
19749                 $this->endlinex = $this->img_rb_x;
19750         }
19751 
19771         public function getMargins() {
19772                 $ret = array(
19773                         'left' => $this->lMargin,
19774                         'right' => $this->rMargin,
19775                         'top' => $this->tMargin,
19776                         'bottom' => $this->bMargin,
19777                         'header' => $this->header_margin,
19778                         'footer' => $this->footer_margin,
19779                         'cell' => $this->cell_padding,
19780                         'padding_left' => $this->cell_padding['L'],
19781                         'padding_top' => $this->cell_padding['T'],
19782                         'padding_right' => $this->cell_padding['R'],
19783                         'padding_bottom' => $this->cell_padding['B']
19784                 );
19785                 return $ret;
19786         }
19787 
19798         public function getOriginalMargins() {
19799                 $ret = array(
19800                         'left' => $this->original_lMargin,
19801                         'right' => $this->original_rMargin
19802                 );
19803                 return $ret;
19804         }
19805 
19812         public function getFontSize() {
19813                 return $this->FontSize;
19814         }
19815 
19822         public function getFontSizePt() {
19823                 return $this->FontSizePt;
19824         }
19825 
19832         public function getFontFamily() {
19833                 return $this->FontFamily;
19834         }
19835 
19842         public function getFontStyle() {
19843                 return $this->FontStyle;
19844         }
19845 
19858         public function fixHTMLCode($html, $default_css='', $tagvs='', $tidy_options='') {
19859                 // configure parameters for HTML Tidy
19860                 if ($tidy_options === '') {
19861                         $tidy_options = array (
19862                                 'clean' => 1,
19863                                 'drop-empty-paras' => 0,
19864                                 'drop-proprietary-attributes' => 1,
19865                                 'fix-backslash' => 1,
19866                                 'hide-comments' => 1,
19867                                 'join-styles' => 1,
19868                                 'lower-literals' => 1,
19869                                 'merge-divs' => 1,
19870                                 'merge-spans' => 1,
19871                                 'output-xhtml' => 1,
19872                                 'word-2000' => 1,
19873                                 'wrap' => 0,
19874                                 'output-bom' => 0,
19875                                 //'char-encoding' => 'utf8',
19876                                 //'input-encoding' => 'utf8',
19877                                 //'output-encoding' => 'utf8'
19878                         );
19879                 }
19880                 // clean up the HTML code
19881                 $tidy = tidy_parse_string($html, $tidy_options);
19882                 // fix the HTML
19883                 $tidy->cleanRepair();
19884                 // get the CSS part
19885                 $tidy_head = tidy_get_head($tidy);
19886                 $css = $tidy_head->value;
19887                 $css = preg_replace('/<style([^>]+)>/ims', '<style>', $css);
19888                 $css = preg_replace('/<\/style>(.*)<style>/ims', "\n", $css);
19889                 $css = str_replace('/*<![CDATA[*/', '', $css);
19890                 $css = str_replace('/*]]>*/', '', $css);
19891                 preg_match('/<style>(.*)<\/style>/ims', $css, $matches);
19892                 if (isset($matches[1])) {
19893                         $css = strtolower($matches[1]);
19894                 } else {
19895                         $css = '';
19896                 }
19897                 // include default css
19898                 $css = '<style>'.$default_css.$css.'</style>';
19899                 // get the body part
19900                 $tidy_body = tidy_get_body($tidy);
19901                 $html = $tidy_body->value;
19902                 // fix some self-closing tags
19903                 $html = str_replace('<br>', '<br />', $html);
19904                 // remove some empty tag blocks
19905                 $html = preg_replace('/<div([^>]*)><\/div>/', '', $html);
19906                 $html = preg_replace('/<p([^>]*)><\/p>/', '', $html);
19907                 if ($tagvs !== '') {
19908                         // set vertical space for some XHTML tags
19909                         $this->setHtmlVSpace($tagvs);
19910                 }
19911                 // return the cleaned XHTML code + CSS
19912                 return $css.$html;
19913         }
19914 
19923         protected function extractCSSproperties($cssdata) {
19924                 if (empty($cssdata)) {
19925                         return array();
19926                 }
19927                 // remove comments
19928                 $cssdata = preg_replace('/\/\*[^\*]*\*\//', '', $cssdata);
19929                 // remove newlines and multiple spaces
19930                 $cssdata = preg_replace('/[\s]+/', ' ', $cssdata);
19931                 // remove some spaces
19932                 $cssdata = preg_replace('/[\s]*([;:\{\}]{1})[\s]*/', '\\1', $cssdata);
19933                 // remove empty blocks
19934                 $cssdata = preg_replace('/([^\}\{]+)\{\}/', '', $cssdata);
19935                 // replace media type parenthesis
19936                 $cssdata = preg_replace('/@media[\s]+([^\{]*)\{/i', '@media \\1§', $cssdata);
19937                 $cssdata = preg_replace('/\}\}/si', '}§', $cssdata);
19938                 // trim string
19939                 $cssdata = trim($cssdata);
19940                 // find media blocks (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
19941                 $cssblocks = array();
19942                 $matches = array();
19943                 if (preg_match_all('/@media[\s]+([^\§]*)§([^§]*)§/i', $cssdata, $matches) > 0) {
19944                         foreach ($matches[1] as $key => $type) {
19945                                 $cssblocks[$type] = $matches[2][$key];
19946                         }
19947                         // remove media blocks
19948                         $cssdata = preg_replace('/@media[\s]+([^\§]*)§([^§]*)§/i', '', $cssdata);
19949                 }
19950                 // keep 'all' and 'print' media, other media types are discarded
19951                 if (isset($cssblocks['all']) AND !empty($cssblocks['all'])) {
19952                         $cssdata .= $cssblocks['all'];
19953                 }
19954                 if (isset($cssblocks['print']) AND !empty($cssblocks['print'])) {
19955                         $cssdata .= $cssblocks['print'];
19956                 }
19957                 // reset css blocks array
19958                 $cssblocks = array();
19959                 $matches = array();
19960                 // explode css data string into array
19961                 if (substr($cssdata, -1) == '}') {
19962                         // remove last parethesis
19963                         $cssdata = substr($cssdata, 0, -1);
19964                 }
19965                 $matches = explode('}', $cssdata);
19966                 foreach ($matches as $key => $block) {
19967                         // index 0 contains the CSS selector, index 1 contains CSS properties
19968                         $cssblocks[$key] = explode('{', $block);
19969                         if (!isset($cssblocks[$key][1])) {
19970                                 // remove empty definitions
19971                                 unset($cssblocks[$key]);
19972                         }
19973                 }
19974                 // split groups of selectors (comma-separated list of selectors)
19975                 foreach ($cssblocks as $key => $block) {
19976                         if (strpos($block[0], ',') > 0) {
19977                                 $selectors = explode(',', $block[0]);
19978                                 foreach ($selectors as $sel) {
19979                                         $cssblocks[] = array(0 => trim($sel), 1 => $block[1]);
19980                                 }
19981                                 unset($cssblocks[$key]);
19982                         }
19983                 }
19984                 // covert array to selector => properties
19985                 $cssdata = array();
19986                 foreach ($cssblocks as $block) {
19987                         $selector = $block[0];
19988                         // calculate selector's specificity
19989                         $matches = array();
19990                         $a = 0; // the declaration is not from is a 'style' attribute
19991                         $b = intval(preg_match_all('/[\#]/', $selector, $matches)); // number of ID attributes
19992                         $c = intval(preg_match_all('/[\[\.]/', $selector, $matches)); // number of other attributes
19993                         $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
19994                         $d = intval(preg_match_all('/[>\+\~\s]{1}[a-zA-Z0-9]+/', ' '.$selector, $matches)); // number of element names
19995                         $d += intval(preg_match_all('/[\:][\:]/', $selector, $matches)); // number of pseudo-elements
19996                         $specificity = $a.$b.$c.$d;
19997                         // add specificity to the beginning of the selector
19998                         $cssdata[$specificity.' '.$selector] = $block[1];
19999                 }
20000                 // sort selectors alphabetically to account for specificity
20001                 ksort($cssdata, SORT_STRING);
20002                 // return array
20003                 return $cssdata;
20004         }
20005 
20015         protected function isValidCSSSelectorForTag($dom, $key, $selector) {
20016                 $valid = false; // value to be returned
20017                 $tag = $dom[$key]['value'];
20018                 $class = array();
20019                 if (isset($dom[$key]['attribute']['class']) AND !empty($dom[$key]['attribute']['class'])) {
20020                         $class = explode(' ', strtolower($dom[$key]['attribute']['class']));
20021                 }
20022                 $id = '';
20023                 if (isset($dom[$key]['attribute']['id']) AND !empty($dom[$key]['attribute']['id'])) {
20024                         $id = strtolower($dom[$key]['attribute']['id']);
20025                 }
20026                 $selector = preg_replace('/([>\+\~\s]{1})([\.]{1})([^>\+\~\s]*)/si', '\\1*.\\3', $selector);
20027                 $matches = array();
20028                 if (preg_match_all('/([>\+\~\s]{1})([a-zA-Z0-9\*]+)([^>\+\~\s]*)/si', $selector, $matches, PREG_PATTERN_ORDER | PREG_OFFSET_CAPTURE) > 0) {
20029                         $parentop = array_pop($matches[1]);
20030                         $operator = $parentop[0];
20031                         $offset = $parentop[1];
20032                         $lasttag = array_pop($matches[2]);
20033                         $lasttag = strtolower(trim($lasttag[0]));
20034                         if (($lasttag == '*') OR ($lasttag == $tag)) {
20035                                 // the last element on selector is our tag or 'any tag'
20036                                 $attrib = array_pop($matches[3]);
20037                                 $attrib = strtolower(trim($attrib[0]));
20038                                 if (!empty($attrib)) {
20039                                         // check if matches class, id, attribute, pseudo-class or pseudo-element
20040                                         switch ($attrib{0}) {
20041                                                 case '.': { // class
20042                                                         if (in_array(substr($attrib, 1), $class)) {
20043                                                                 $valid = true;
20044                                                         }
20045                                                         break;
20046                                                 }
20047                                                 case '#': { // ID
20048                                                         if (substr($attrib, 1) == $id) {
20049                                                                 $valid = true;
20050                                                         }
20051                                                         break;
20052                                                 }
20053                                                 case '[': { // attribute
20054                                                         $attrmatch = array();
20055                                                         if (preg_match('/\[([a-zA-Z0-9]*)[\s]*([\~\^\$\*\|\=]*)[\s]*["]?([^"\]]*)["]?\]/i', $attrib, $attrmatch) > 0) {
20056                                                                 $att = strtolower($attrmatch[1]);
20057                                                                 $val = $attrmatch[3];
20058                                                                 if (isset($dom[$key]['attribute'][$att])) {
20059                                                                         switch ($attrmatch[2]) {
20060                                                                                 case '=': {
20061                                                                                         if ($dom[$key]['attribute'][$att] == $val) {
20062                                                                                                 $valid = true;
20063                                                                                         }
20064                                                                                         break;
20065                                                                                 }
20066                                                                                 case '~=': {
20067                                                                                         if (in_array($val, explode(' ', $dom[$key]['attribute'][$att]))) {
20068                                                                                                 $valid = true;
20069                                                                                         }
20070                                                                                         break;
20071                                                                                 }
20072                                                                                 case '^=': {
20073                                                                                         if ($val == substr($dom[$key]['attribute'][$att], 0, strlen($val))) {
20074                                                                                                 $valid = true;
20075                                                                                         }
20076                                                                                         break;
20077                                                                                 }
20078                                                                                 case '$=': {
20079                                                                                         if ($val == substr($dom[$key]['attribute'][$att], -strlen($val))) {
20080                                                                                                 $valid = true;
20081                                                                                         }
20082                                                                                         break;
20083                                                                                 }
20084                                                                                 case '*=': {
20085                                                                                         if (strpos($dom[$key]['attribute'][$att], $val) !== false) {
20086                                                                                                 $valid = true;
20087                                                                                         }
20088                                                                                         break;
20089                                                                                 }
20090                                                                                 case '|=': {
20091                                                                                         if ($dom[$key]['attribute'][$att] == $val) {
20092                                                                                                 $valid = true;
20093                                                                                         } elseif (preg_match('/'.$val.'[\-]{1}/i', $dom[$key]['attribute'][$att]) > 0) {
20094                                                                                                 $valid = true;
20095                                                                                         }
20096                                                                                         break;
20097                                                                                 }
20098                                                                                 default: {
20099                                                                                         $valid = true;
20100                                                                                 }
20101                                                                         }
20102                                                                 }
20103                                                         }
20104                                                         break;
20105                                                 }
20106                                                 case ':': { // pseudo-class or pseudo-element
20107                                                         if ($attrib{1} == ':') { // pseudo-element
20108                                                                 // pseudo-elements are not supported!
20109                                                                 // (::first-line, ::first-letter, ::before, ::after)
20110                                                         } else { // pseudo-class
20111                                                                 // pseudo-classes are not supported!
20112                                                                 // (: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)
20113                                                         }
20114                                                         break;
20115                                                 }
20116                                         } // end of switch
20117                                 } else {
20118                                         $valid = true;
20119                                 }
20120                                 if ($valid AND ($offset > 0)) {
20121                                         $valid = false;
20122                                         // check remaining selector part
20123                                         $selector = substr($selector, 0, $offset);
20124                                         switch ($operator) {
20125                                                 case ' ': { // descendant of an element
20126                                                         while ($dom[$key]['parent'] > 0) {
20127                                                                 if ($this->isValidCSSSelectorForTag($dom, $dom[$key]['parent'], $selector)) {
20128                                                                         $valid = true;
20129                                                                         break;
20130                                                                 } else {
20131                                                                         $key = $dom[$key]['parent'];
20132                                                                 }
20133                                                         }
20134                                                         break;
20135                                                 }
20136                                                 case '>': { // child of an element
20137                                                         $valid = $this->isValidCSSSelectorForTag($dom, $dom[$key]['parent'], $selector);
20138                                                         break;
20139                                                 }
20140                                                 case '+': { // immediately preceded by an element
20141                                                         for ($i = ($key - 1); $i > $dom[$key]['parent']; --$i) {
20142                                                                 if ($dom[$i]['tag'] AND $dom[$i]['opening']) {
20143                                                                         $valid = $this->isValidCSSSelectorForTag($dom, $i, $selector);
20144                                                                         break;
20145                                                                 }
20146                                                         }
20147                                                         break;
20148                                                 }
20149                                                 case '~': { // preceded by an element
20150                                                         for ($i = ($key - 1); $i > $dom[$key]['parent']; --$i) {
20151                                                                 if ($dom[$i]['tag'] AND $dom[$i]['opening']) {
20152                                                                         if ($this->isValidCSSSelectorForTag($dom, $i, $selector)) {
20153                                                                                 break;
20154                                                                         }
20155                                                                 }
20156                                                         }
20157                                                         break;
20158                                                 }
20159                                         }
20160                                 }
20161                         }
20162                 }
20163                 return $valid;
20164         }
20165 
20175         protected function getCSSdataArray($dom, $key, $css) {
20176                 $cssarray = array(); // style to be returned
20177                 // get parent CSS selectors
20178                 $selectors = array();
20179                 if (isset($dom[($dom[$key]['parent'])]['csssel'])) {
20180                         $selectors = $dom[($dom[$key]['parent'])]['csssel'];
20181                 }
20182                 // get all styles that apply
20183                 foreach($css as $selector => $style) {
20184                         $pos = strpos($selector, ' ');
20185                         // get specificity
20186                         $specificity = substr($selector, 0, $pos);
20187                         // remove specificity
20188                         $selector = substr($selector, $pos);
20189                         // check if this selector apply to current tag
20190                         if ($this->isValidCSSSelectorForTag($dom, $key, $selector)) {
20191                                 if (!in_array($selector, $selectors)) {
20192                                         // add style if not already added on parent selector
20193                                         $cssarray[] = array('k' => $selector, 's' => $specificity, 'c' => $style);
20194                                         $selectors[] = $selector;
20195                                 }
20196                         }
20197                 }
20198                 if (isset($dom[$key]['attribute']['style'])) {
20199                         // attach inline style (latest properties have high priority)
20200                         $cssarray[] = array('k' => '', 's' => '1000', 'c' => $dom[$key]['attribute']['style']);
20201                 }
20202                 // order the css array to account for specificity
20203                 $cssordered = array();
20204                 foreach ($cssarray as $key => $val) {
20205                         $skey = sprintf('%04d', $key);
20206                         $cssordered[$val['s'].'_'.$skey] = $val;
20207                 }
20208                 // sort selectors alphabetically to account for specificity
20209                 ksort($cssordered, SORT_STRING);
20210                 return array($selectors, $cssordered);
20211         }
20212 
20220         protected function getTagStyleFromCSSarray($css) {
20221                 $tagstyle = ''; // value to be returned
20222                 foreach ($css as $style) {
20223                         // split single css commands
20224                         $csscmds = explode(';', $style['c']);
20225                         foreach ($csscmds as $cmd) {
20226                                 if (!empty($cmd)) {
20227                                         $pos = strpos($cmd, ':');
20228                                         if ($pos !== false) {
20229                                                 $cmd = substr($cmd, 0, ($pos + 1));
20230                                                 if (strpos($tagstyle, $cmd) !== false) {
20231                                                         // remove duplicate commands (last commands have high priority)
20232                                                         $tagstyle = preg_replace('/'.$cmd.'[^;]+/i', '', $tagstyle);
20233                                                 }
20234                                         }
20235                                 }
20236                         }
20237                         $tagstyle .= ';'.$style['c'];
20238                 }
20239                 // remove multiple semicolons
20240                 $tagstyle = preg_replace('/[;]+/', ';', $tagstyle);
20241                 return $tagstyle;
20242         }
20243 
20251         protected function getCSSBorderWidth($width) {
20252                 if ($width == 'thin') {
20253                         $width = (2 / $this->k);
20254                 } elseif ($width == 'medium') {
20255                         $width = (4 / $this->k);
20256                 } elseif ($width == 'thick') {
20257                         $width = (6 / $this->k);
20258                 } else {
20259                         $width = $this->getHTMLUnitToUnits($width, 1, 'px', false);
20260                 }
20261                 return $width;
20262         }
20263 
20271         protected function getCSSBorderDashStyle($style) {
20272                 switch (strtolower($style)) {
20273                         case 'none':
20274                         case 'hidden': {
20275                                 $dash = -1;
20276                                 break;
20277                         }
20278                         case 'dotted': {
20279                                 $dash = 1;
20280                                 break;
20281                         }
20282                         case 'dashed': {
20283                                 $dash = 3;
20284                                 break;
20285                         }
20286                         case 'double':
20287                         case 'groove':
20288                         case 'ridge':
20289                         case 'inset':
20290                         case 'outset':
20291                         case 'solid':
20292                         default: {
20293                                 $dash = 0;
20294                                 break;
20295                         }
20296                 }
20297                 return $dash;
20298         }
20299 
20307         protected function getCSSBorderStyle($cssborder) {
20308                 $bprop = preg_split('/[\s]+/', trim($cssborder));
20309                 $border = array(); // value to be returned
20310                 switch (count($bprop)) {
20311                         case 3: {
20312                                 $width = $bprop[0];
20313                                 $style = $bprop[1];
20314                                 $color = $bprop[2];
20315                                 break;
20316                         }
20317                         case 2: {
20318                                 $width = 'medium';
20319                                 $style = $bprop[0];
20320                                 $color = $bprop[1];
20321                                 break;
20322                         }
20323                         case 1: {
20324                                 $width = 'medium';
20325                                 $style = $bprop[0];
20326                                 $color = 'black';
20327                                 break;
20328                         }
20329                         default: {
20330                                 $width = 'medium';
20331                                 $style = 'solid';
20332                                 $color = 'black';
20333                                 break;
20334                         }
20335                 }
20336                 if ($style == 'none') {
20337                         return array();
20338                 }
20339                 $border['cap'] = 'square';
20340                 $border['join'] = 'miter';
20341                 $border['dash'] = $this->getCSSBorderDashStyle($style);
20342                 if ($border['dash'] < 0) {
20343                         return array();
20344                 }
20345                 $border['width'] = $this->getCSSBorderWidth($width);
20346                 $border['color'] = $this->convertHTMLColorToDec($color);
20347                 return $border;
20348         }
20349 
20358         public function getCSSPadding($csspadding, $width=0) {
20359                 $padding = preg_split('/[\s]+/', trim($csspadding));
20360                 $cell_padding = array(); // value to be returned
20361                 switch (count($padding)) {
20362                         case 4: {
20363                                 $cell_padding['T'] = $padding[0];
20364                                 $cell_padding['R'] = $padding[1];
20365                                 $cell_padding['B'] = $padding[2];
20366                                 $cell_padding['L'] = $padding[3];
20367                                 break;
20368                         }
20369                         case 3: {
20370                                 $cell_padding['T'] = $padding[0];
20371                                 $cell_padding['R'] = $padding[1];
20372                                 $cell_padding['B'] = $padding[2];
20373                                 $cell_padding['L'] = $padding[1];
20374                                 break;
20375                         }
20376                         case 2: {
20377                                 $cell_padding['T'] = $padding[0];
20378                                 $cell_padding['R'] = $padding[1];
20379                                 $cell_padding['B'] = $padding[0];
20380                                 $cell_padding['L'] = $padding[1];
20381                                 break;
20382                         }
20383                         case 1: {
20384                                 $cell_padding['T'] = $padding[0];
20385                                 $cell_padding['R'] = $padding[0];
20386                                 $cell_padding['B'] = $padding[0];
20387                                 $cell_padding['L'] = $padding[0];
20388                                 break;
20389                         }
20390                         default: {
20391                                 return $this->cell_padding;
20392                         }
20393                 }
20394                 if ($width == 0) {
20395                         $width = $this->w - $this->lMargin - $this->rMargin;
20396                 }
20397                 $cell_padding['T'] = $this->getHTMLUnitToUnits($cell_padding['T'], $width, 'px', false);
20398                 $cell_padding['R'] = $this->getHTMLUnitToUnits($cell_padding['R'], $width, 'px', false);
20399                 $cell_padding['B'] = $this->getHTMLUnitToUnits($cell_padding['B'], $width, 'px', false);
20400                 $cell_padding['L'] = $this->getHTMLUnitToUnits($cell_padding['L'], $width, 'px', false);
20401                 return $cell_padding;
20402         }
20403 
20412         public function getCSSMargin($cssmargin, $width=0) {
20413                 $margin = preg_split('/[\s]+/', trim($cssmargin));
20414                 $cell_margin = array(); // value to be returned
20415                 switch (count($margin)) {
20416                         case 4: {
20417                                 $cell_margin['T'] = $margin[0];
20418                                 $cell_margin['R'] = $margin[1];
20419                                 $cell_margin['B'] = $margin[2];
20420                                 $cell_margin['L'] = $margin[3];
20421                                 break;
20422                         }
20423                         case 3: {
20424                                 $cell_margin['T'] = $margin[0];
20425                                 $cell_margin['R'] = $margin[1];
20426                                 $cell_margin['B'] = $margin[2];
20427                                 $cell_margin['L'] = $margin[1];
20428                                 break;
20429                         }
20430                         case 2: {
20431                                 $cell_margin['T'] = $margin[0];
20432                                 $cell_margin['R'] = $margin[1];
20433                                 $cell_margin['B'] = $margin[0];
20434                                 $cell_margin['L'] = $margin[1];
20435                                 break;
20436                         }
20437                         case 1: {
20438                                 $cell_margin['T'] = $margin[0];
20439                                 $cell_margin['R'] = $margin[0];
20440                                 $cell_margin['B'] = $margin[0];
20441                                 $cell_margin['L'] = $margin[0];
20442                                 break;
20443                         }
20444                         default: {
20445                                 return $this->cell_margin;
20446                         }
20447                 }
20448                 if ($width == 0) {
20449                         $width = $this->w - $this->lMargin - $this->rMargin;
20450                 }
20451                 $cell_margin['T'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['T']), $width, 'px', false);
20452                 $cell_margin['R'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['R']), $width, 'px', false);
20453                 $cell_margin['B'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['B']), $width, 'px', false);
20454                 $cell_margin['L'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['L']), $width, 'px', false);
20455                 return $cell_margin;
20456         }
20457 
20466         public function getCSSBorderMargin($cssbspace, $width=0) {
20467                 $space = preg_split('/[\s]+/', trim($cssbspace));
20468                 $border_spacing = array(); // value to be returned
20469                 switch (count($space)) {
20470                         case 2: {
20471                                 $border_spacing['H'] = $space[0];
20472                                 $border_spacing['V'] = $space[1];
20473                                 break;
20474                         }
20475                         case 1: {
20476                                 $border_spacing['H'] = $space[0];
20477                                 $border_spacing['V'] = $space[0];
20478                                 break;
20479                         }
20480                         default: {
20481                                 return array('H' => 0, 'V' => 0);
20482                         }
20483                 }
20484                 if ($width == 0) {
20485                         $width = $this->w - $this->lMargin - $this->rMargin;
20486                 }
20487                 $border_spacing['H'] = $this->getHTMLUnitToUnits($border_spacing['H'], $width, 'px', false);
20488                 $border_spacing['V'] = $this->getHTMLUnitToUnits($border_spacing['V'], $width, 'px', false);
20489                 return $border_spacing;
20490         }
20491 
20500         protected function getCSSFontSpacing($spacing, $parent=0) {
20501                 $val = 0; // value to be returned
20502                 $spacing = trim($spacing);
20503                 switch ($spacing) {
20504                         case 'normal': {
20505                                 $val = 0;
20506                                 break;
20507                         }
20508                         case 'inherit': {
20509                                 if ($parent == 'normal') {
20510                                         $val = 0;
20511                                 } else {
20512                                         $val = $parent;
20513                                 }
20514                                 break;
20515                         }
20516                         default: {
20517                                 $val = $this->getHTMLUnitToUnits($spacing, 0, 'px', false);
20518                         }
20519                 }
20520                 return $val;
20521         }
20522 
20531         protected function getCSSFontStretching($stretch, $parent=100) {
20532                 $val = 100; // value to be returned
20533                 $stretch = trim($stretch);
20534                 switch ($stretch) {
20535                         case 'ultra-condensed': {
20536                                 $val = 40;
20537                                 break;
20538                         }
20539                         case 'extra-condensed': {
20540                                 $val = 55;
20541                                 break;
20542                         }
20543                         case 'condensed': {
20544                                 $val = 70;
20545                                 break;
20546                         }
20547                         case 'semi-condensed': {
20548                                 $val = 85;
20549                                 break;
20550                         }
20551                         case 'normal': {
20552                                 $val = 100;
20553                                 break;
20554                         }
20555                         case 'semi-expanded': {
20556                                 $val = 115;
20557                                 break;
20558                         }
20559                         case 'expanded': {
20560                                 $val = 130;
20561                                 break;
20562                         }
20563                         case 'extra-expanded': {
20564                                 $val = 145;
20565                                 break;
20566                         }
20567                         case 'ultra-expanded': {
20568                                 $val = 160;
20569                                 break;
20570                         }
20571                         case 'wider': {
20572                                 $val = $parent + 10;
20573                                 break;
20574                         }
20575                         case 'narrower': {
20576                                 $val = $parent - 10;
20577                                 break;
20578                         }
20579                         case 'inherit': {
20580                                 if ($parent == 'normal') {
20581                                         $val = 100;
20582                                 } else {
20583                                         $val = $parent;
20584                                 }
20585                                 break;
20586                         }
20587                         default: {
20588                                 $val = $this->getHTMLUnitToUnits($stretch, 100, '%', false);
20589                         }
20590                 }
20591                 return $val;
20592         }
20593 
20601         protected function getHtmlDomArray($html) {
20602                 // array of CSS styles ( selector => properties).
20603                 $css = array();
20604                 // get CSS array defined at previous call
20605                 $matches = array();
20606                 if (preg_match_all('/<cssarray>([^<]*)<\/cssarray>/isU', $html, $matches) > 0) {
20607                         if (isset($matches[1][0])) {
20608                                 $css = array_merge($css, unserialize($this->unhtmlentities($matches[1][0])));
20609                         }
20610                         $html = preg_replace('/<cssarray>(.*?)<\/cssarray>/isU', '', $html);
20611                 }
20612                 // extract external CSS files
20613                 $matches = array();
20614                 if (preg_match_all('/<link([^>]*)>/isU', $html, $matches) > 0) {
20615                         foreach ($matches[1] as $key => $link) {
20616                                 $type = array();
20617                                 if (preg_match('/type[\s]*=[\s]*"text\/css"/', $link, $type)) {
20618                                         $type = array();
20619                                         preg_match('/media[\s]*=[\s]*"([^"]*)"/', $link, $type);
20620                                         // get 'all' and 'print' media, other media types are discarded
20621                                         // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
20622                                         if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
20623                                                 $type = array();
20624                                                 if (preg_match('/href[\s]*=[\s]*"([^"]*)"/', $link, $type) > 0) {
20625                                                         // read CSS data file
20626                                                         $cssdata = file_get_contents(trim($type[1]));
20627                                                         $css = array_merge($css, $this->extractCSSproperties($cssdata));
20628                                                 }
20629                                         }
20630                                 }
20631                         }
20632                 }
20633                 // extract style tags
20634                 $matches = array();
20635                 if (preg_match_all('/<style([^>]*)>([^<]*)<\/style>/isU', $html, $matches) > 0) {
20636                         foreach ($matches[1] as $key => $media) {
20637                                 $type = array();
20638                                 preg_match('/media[\s]*=[\s]*"([^"]*)"/', $media, $type);
20639                                 // get 'all' and 'print' media, other media types are discarded
20640                                 // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
20641                                 if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
20642                                         $cssdata = $matches[2][$key];
20643                                         $css = array_merge($css, $this->extractCSSproperties($cssdata));
20644                                 }
20645                         }
20646                 }
20647                 // create a special tag to contain the CSS array (used for table content)
20648                 $csstagarray = '<cssarray>'.htmlentities(serialize($css)).'</cssarray>';
20649                 // remove head and style blocks
20650                 $html = preg_replace('/<head([^>]*)>(.*?)<\/head>/siU', '', $html);
20651                 $html = preg_replace('/<style([^>]*)>([^<]*)<\/style>/isU', '', $html);
20652                 // define block tags
20653                 $blocktags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table','tr','td');
20654                 // define self-closing tags
20655                 $selfclosingtags = array('area','base','basefont','br','hr','input','img','link','meta');
20656                 // remove all unsupported tags (the line below lists all supported tags)
20657                 $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>');
20658                 //replace some blank characters
20659                 $html = preg_replace('/<pre/', '<xre', $html); // preserve pre tag
20660                 $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);
20661                 $html = preg_replace('@(\r\n|\r)@', "\n", $html);
20662                 $repTable = array("\t" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\");
20663                 $html = strtr($html, $repTable);
20664                 $offset = 0;
20665                 while (($offset < strlen($html)) AND ($pos = strpos($html, '</pre>', $offset)) !== false) {
20666                         $html_a = substr($html, 0, $offset);
20667                         $html_b = substr($html, $offset, ($pos - $offset + 6));
20668                         while (preg_match("'<xre([^>]*)>(.*?)\n(.*?)</pre>'si", $html_b)) {
20669                                 // preserve newlines on <pre> tag
20670                                 $html_b = preg_replace("'<xre([^>]*)>(.*?)\n(.*?)</pre>'si", "<xre\\1>\\2<br />\\3</pre>", $html_b);
20671                         }
20672                         while (preg_match("'<xre([^>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], $html_b)) {
20673                                 // preserve spaces on <pre> tag
20674                                 $html_b = preg_replace("'<xre([^>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], "<xre\\1>\\2&nbsp;\\3</pre>", $html_b);
20675                         }
20676                         $html = $html_a.$html_b.substr($html, $pos + 6);
20677                         $offset = strlen($html_a.$html_b);
20678                 }
20679                 $offset = 0;
20680                 while (($offset < strlen($html)) AND ($pos = strpos($html, '</textarea>', $offset)) !== false) {
20681                         $html_a = substr($html, 0, $offset);
20682                         $html_b = substr($html, $offset, ($pos - $offset + 11));
20683                         while (preg_match("'<textarea([^>]*)>(.*?)\n(.*?)</textarea>'si", $html_b)) {
20684                                 // preserve newlines on <textarea> tag
20685                                 $html_b = preg_replace("'<textarea([^>]*)>(.*?)\n(.*?)</textarea>'si", "<textarea\\1>\\2<TBR>\\3</textarea>", $html_b);
20686                                 $html_b = preg_replace("'<textarea([^>]*)>(.*?)[\"](.*?)</textarea>'si", "<textarea\\1>\\2''\\3</textarea>", $html_b);
20687                         }
20688                         $html = $html_a.$html_b.substr($html, $pos + 11);
20689                         $offset = strlen($html_a.$html_b);
20690                 }
20691                 $html = preg_replace('/([\s]*)<option/si', '<option', $html);
20692                 $html = preg_replace('/<\/option>([\s]*)/si', '</option>', $html);
20693                 $offset = 0;
20694                 while (($offset < strlen($html)) AND ($pos = strpos($html, '</option>', $offset)) !== false) {
20695                         $html_a = substr($html, 0, $offset);
20696                         $html_b = substr($html, $offset, ($pos - $offset + 9));
20697                         while (preg_match("'<option([^>]*)>(.*?)</option>'si", $html_b)) {
20698                                 $html_b = preg_replace("'<option([\s]+)value=\"([^\"]*)\"([^>]*)>(.*?)</option>'si", "\\2#!TaB!#\\4#!NwL!#", $html_b);
20699                                 $html_b = preg_replace("'<option([^>]*)>(.*?)</option>'si", "\\2#!NwL!#", $html_b);
20700                         }
20701                         $html = $html_a.$html_b.substr($html, $pos + 9);
20702                         $offset = strlen($html_a.$html_b);
20703                 }
20704                 if (preg_match("'</select'si", $html)) {
20705                         $html = preg_replace("'<select([^>]*)>'si", "<select\\1 opt=\"", $html);
20706                         $html = preg_replace("'#!NwL!#</select>'si", "\" />", $html);
20707                 }
20708                 $html = str_replace("\n", ' ', $html);
20709                 // restore textarea newlines
20710                 $html = str_replace('<TBR>', "\n", $html);
20711                 // remove extra spaces from code
20712                 $html = preg_replace('/[\s]+<\/(table|tr|ul|ol|dl)>/', '</\\1>', $html);
20713                 $html = preg_replace('/'.$this->re_space['p'].'+<\/(td|th|li|dt|dd)>/'.$this->re_space['m'], '</\\1>', $html);
20714                 $html = preg_replace('/[\s]+<(tr|td|th|li|dt|dd)/', '<\\1', $html);
20715                 $html = preg_replace('/'.$this->re_space['p'].'+<(ul|ol|dl|br)/'.$this->re_space['m'], '<\\1', $html);
20716                 $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);
20717                 $html = preg_replace('/<\/(td|th)>/', '<marker style="font-size:0"/></\\1>', $html);
20718                 $html = preg_replace('/<\/table>([\s]*)<marker style="font-size:0"\/>/', '</table>', $html);
20719                 $html = preg_replace('/'.$this->re_space['p'].'+<img/'.$this->re_space['m'], chr(32).'<img', $html);
20720                 $html = preg_replace('/<img([^>]*)>[\s]+([^<])/xi', '<img\\1>&nbsp;\\2', $html);
20721                 $html = preg_replace('/<img([^>]*)>/xi', '<img\\1><span><marker style="font-size:0"/></span>', $html);
20722                 $html = preg_replace('/<xre/', '<pre', $html); // restore pre tag
20723                 $html = preg_replace('/<textarea([^>]*)>([^<]*)<\/textarea>/xi', '<textarea\\1 value="\\2" />', $html);
20724                 $html = preg_replace('/<li([^>]*)><\/li>/', '<li\\1>&nbsp;</li>', $html);
20725                 $html = preg_replace('/<li([^>]*)>'.$this->re_space['p'].'*<img/'.$this->re_space['m'], '<li\\1><font size="1">&nbsp;</font><img', $html);
20726                 $html = preg_replace('/<([^>\/]*)>[\s]/', '<\\1>&nbsp;', $html); // preserve some spaces
20727                 $html = preg_replace('/[\s]<\/([^>]*)>/', '&nbsp;</\\1>', $html); // preserve some spaces
20728                 $html = preg_replace('/'.$this->re_space['p'].'+/'.$this->re_space['m'], chr(32), $html); // replace multiple spaces with a single space
20729                 // trim string
20730                 $html = $this->stringTrim($html);
20731                 // fix first image tag alignment
20732                 $html = preg_replace('/^<img/', '<span style="font-size:0"><br /></span> <img', $html, 1);
20733                 // pattern for generic tag
20734                 $tagpattern = '/(<[^>]+>)/';
20735                 // explodes the string
20736                 $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
20737                 // count elements
20738                 $maxel = count($a);
20739                 $elkey = 0;
20740                 $key = 0;
20741                 // create an array of elements
20742                 $dom = array();
20743                 $dom[$key] = array();
20744                 // set inheritable properties fot the first void element
20745                 // 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
20746                 $dom[$key]['tag'] = false;
20747                 $dom[$key]['block'] = false;
20748                 $dom[$key]['value'] = '';
20749                 $dom[$key]['parent'] = 0;
20750                 $dom[$key]['hide'] = false;
20751                 $dom[$key]['fontname'] = $this->FontFamily;
20752                 $dom[$key]['fontstyle'] = $this->FontStyle;
20753                 $dom[$key]['fontsize'] = $this->FontSizePt;
20754                 $dom[$key]['font-stretch'] = $this->font_stretching;
20755                 $dom[$key]['letter-spacing'] = $this->font_spacing;
20756                 $dom[$key]['stroke'] = $this->textstrokewidth;
20757                 $dom[$key]['fill'] = (($this->textrendermode % 2) == 0);
20758                 $dom[$key]['clip'] = ($this->textrendermode > 3);
20759                 $dom[$key]['line-height'] = $this->cell_height_ratio;
20760                 $dom[$key]['bgcolor'] = false;
20761                 $dom[$key]['fgcolor'] = $this->fgcolor; // color
20762                 $dom[$key]['strokecolor'] = $this->strokecolor;
20763                 $dom[$key]['align'] = '';
20764                 $dom[$key]['listtype'] = '';
20765                 $dom[$key]['text-indent'] = 0;
20766                 $dom[$key]['border'] = array();
20767                 $dom[$key]['dir'] = $this->rtl?'rtl':'ltr';
20768                 $thead = false; // true when we are inside the THEAD tag
20769                 ++$key;
20770                 $level = array();
20771                 array_push($level, 0); // root
20772                 while ($elkey < $maxel) {
20773                         $dom[$key] = array();
20774                         $element = $a[$elkey];
20775                         $dom[$key]['elkey'] = $elkey;
20776                         if (preg_match($tagpattern, $element)) {
20777                                 // html tag
20778                                 $element = substr($element, 1, -1);
20779                                 // get tag name
20780                                 preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag);
20781                                 $tagname = strtolower($tag[1]);
20782                                 // check if we are inside a table header
20783                                 if ($tagname == 'thead') {
20784                                         if ($element{0} == '/') {
20785                                                 $thead = false;
20786                                         } else {
20787                                                 $thead = true;
20788                                         }
20789                                         ++$elkey;
20790                                         continue;
20791                                 }
20792                                 $dom[$key]['tag'] = true;
20793                                 $dom[$key]['value'] = $tagname;
20794                                 if (in_array($dom[$key]['value'], $blocktags)) {
20795                                         $dom[$key]['block'] = true;
20796                                 } else {
20797                                         $dom[$key]['block'] = false;
20798                                 }
20799                                 if ($element{0} == '/') {
20800                                         // *** closing html tag
20801                                         $dom[$key]['opening'] = false;
20802                                         $dom[$key]['parent'] = end($level);
20803                                         array_pop($level);
20804                                         $dom[$key]['hide'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['hide'];
20805                                         $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname'];
20806                                         $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle'];
20807                                         $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize'];
20808                                         $dom[$key]['font-stretch'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['font-stretch'];
20809                                         $dom[$key]['letter-spacing'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['letter-spacing'];
20810                                         $dom[$key]['stroke'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['stroke'];
20811                                         $dom[$key]['fill'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fill'];
20812                                         $dom[$key]['clip'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['clip'];
20813                                         $dom[$key]['line-height'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['line-height'];
20814                                         $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor'];
20815                                         $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor'];
20816                                         $dom[$key]['strokecolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['strokecolor'];
20817                                         $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align'];
20818                                         $dom[$key]['dir'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['dir'];
20819                                         if (isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'])) {
20820                                                 $dom[$key]['listtype'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'];
20821                                         }
20822                                         // set the number of columns in table tag
20823                                         if (($dom[$key]['value'] == 'tr') AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) {
20824                                                 $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols'];
20825                                         }
20826                                         if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
20827                                                 $dom[($dom[$key]['parent'])]['content'] = $csstagarray;
20828                                                 for ($i = ($dom[$key]['parent'] + 1); $i < $key; ++$i) {
20829                                                         $dom[($dom[$key]['parent'])]['content'] .= $a[$dom[$i]['elkey']];
20830                                                 }
20831                                                 $key = $i;
20832                                                 // mark nested tables
20833                                                 $dom[($dom[$key]['parent'])]['content'] = str_replace('<table', '<table nested="true"', $dom[($dom[$key]['parent'])]['content']);
20834                                                 // remove thead sections from nested tables
20835                                                 $dom[($dom[$key]['parent'])]['content'] = str_replace('<thead>', '', $dom[($dom[$key]['parent'])]['content']);
20836                                                 $dom[($dom[$key]['parent'])]['content'] = str_replace('</thead>', '', $dom[($dom[$key]['parent'])]['content']);
20837                                         }
20838                                         // store header rows on a new table
20839                                         if (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['thead'] === true)) {
20840                                                 if ($this->empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) {
20841                                                         $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $csstagarray.$a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']];
20842                                                 }
20843                                                 for ($i = $dom[$key]['parent']; $i <= $key; ++$i) {
20844                                                         $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']];
20845                                                 }
20846                                                 if (!isset($dom[($dom[$key]['parent'])]['attribute'])) {
20847                                                         $dom[($dom[$key]['parent'])]['attribute'] = array();
20848                                                 }
20849                                                 // header elements must be always contained in a single page
20850                                                 $dom[($dom[$key]['parent'])]['attribute']['nobr'] = 'true';
20851                                         }
20852                                         if (($dom[$key]['value'] == 'table') AND (!$this->empty_string($dom[($dom[$key]['parent'])]['thead']))) {
20853                                                 // remove the nobr attributes from the table header
20854                                                 $dom[($dom[$key]['parent'])]['thead'] = str_replace(' nobr="true"', '', $dom[($dom[$key]['parent'])]['thead']);
20855                                                 $dom[($dom[$key]['parent'])]['thead'] .= '</tablehead>';
20856                                         }
20857                                 } else {
20858                                         // *** opening or self-closing html tag
20859                                         $dom[$key]['opening'] = true;
20860                                         $dom[$key]['parent'] = end($level);
20861                                         if ((substr($element, -1, 1) == '/') OR (in_array($dom[$key]['value'], $selfclosingtags))) {
20862                                                 // self-closing tag
20863                                                 $dom[$key]['self'] = true;
20864                                         } else {
20865                                                 // opening tag
20866                                                 array_push($level, $key);
20867                                                 $dom[$key]['self'] = false;
20868                                         }
20869                                         // copy some values from parent
20870                                         $parentkey = 0;
20871                                         if ($key > 0) {
20872                                                 $parentkey = $dom[$key]['parent'];
20873                                                 $dom[$key]['hide'] = $dom[$parentkey]['hide'];
20874                                                 $dom[$key]['fontname'] = $dom[$parentkey]['fontname'];
20875                                                 $dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle'];
20876                                                 $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'];
20877                                                 $dom[$key]['font-stretch'] = $dom[$parentkey]['font-stretch'];
20878                                                 $dom[$key]['letter-spacing'] = $dom[$parentkey]['letter-spacing'];
20879                                                 $dom[$key]['stroke'] = $dom[$parentkey]['stroke'];
20880                                                 $dom[$key]['fill'] = $dom[$parentkey]['fill'];
20881                                                 $dom[$key]['clip'] = $dom[$parentkey]['clip'];
20882                                                 $dom[$key]['line-height'] = $dom[$parentkey]['line-height'];
20883                                                 $dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor'];
20884                                                 $dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor'];
20885                                                 $dom[$key]['strokecolor'] = $dom[$parentkey]['strokecolor'];
20886                                                 $dom[$key]['align'] = $dom[$parentkey]['align'];
20887                                                 $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
20888                                                 $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
20889                                                 $dom[$key]['border'] = array();
20890                                                 $dom[$key]['dir'] = $dom[$parentkey]['dir'];
20891                                         }
20892                                         // get attributes
20893                                         preg_match_all('/([^=\s]*)[\s]*=[\s]*"([^"]*)"/', $element, $attr_array, PREG_PATTERN_ORDER);
20894                                         $dom[$key]['attribute'] = array(); // reset attribute array
20895                                         while (list($id, $name) = each($attr_array[1])) {
20896                                                 $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id];
20897                                         }
20898                                         if (!empty($css)) {
20899                                                 // merge CSS style to current style
20900                                                 list($dom[$key]['csssel'], $dom[$key]['cssdata']) = $this->getCSSdataArray($dom, $key, $css);
20901                                                 $dom[$key]['attribute']['style'] = $this->getTagStyleFromCSSarray($dom[$key]['cssdata']);
20902                                         }
20903                                         // split style attributes
20904                                         if (isset($dom[$key]['attribute']['style']) AND !empty($dom[$key]['attribute']['style'])) {
20905                                                 // get style attributes
20906                                                 preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
20907                                                 $dom[$key]['style'] = array(); // reset style attribute array
20908                                                 while (list($id, $name) = each($style_array[1])) {
20909                                                         // in case of duplicate attribute the last replace the previous
20910                                                         $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]);
20911                                                 }
20912                                                 // --- get some style attributes ---
20913                                                 // text direction
20914                                                 if (isset($dom[$key]['style']['direction'])) {
20915                                                         $dom[$key]['dir'] = $dom[$key]['style']['direction'];
20916                                                 }
20917                                                 // display
20918                                                 if (isset($dom[$key]['style']['display'])) {
20919                                                         $dom[$key]['hide'] = (trim(strtolower($dom[$key]['style']['display'])) == 'none');
20920                                                 }
20921                                                 // font family
20922                                                 if (isset($dom[$key]['style']['font-family'])) {
20923                                                         $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['style']['font-family']);
20924                                                 }
20925                                                 // list-style-type
20926                                                 if (isset($dom[$key]['style']['list-style-type'])) {
20927                                                         $dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type']));
20928                                                         if ($dom[$key]['listtype'] == 'inherit') {
20929                                                                 $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
20930                                                         }
20931                                                 }
20932                                                 // text-indent
20933                                                 if (isset($dom[$key]['style']['text-indent'])) {
20934                                                         $dom[$key]['text-indent'] = $this->getHTMLUnitToUnits($dom[$key]['style']['text-indent']);
20935                                                         if ($dom[$key]['text-indent'] == 'inherit') {
20936                                                                 $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
20937                                                         }
20938                                                 }
20939                                                 // font size
20940                                                 if (isset($dom[$key]['style']['font-size'])) {
20941                                                         $fsize = trim($dom[$key]['style']['font-size']);
20942                                                         switch ($fsize) {
20943                                                                 // absolute-size
20944                                                                 case 'xx-small': {
20945                                                                         $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 4;
20946                                                                         break;
20947                                                                 }
20948                                                                 case 'x-small': {
20949                                                                         $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 3;
20950                                                                         break;
20951                                                                 }
20952                                                                 case 'small': {
20953                                                                         $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 2;
20954                                                                         break;
20955                                                                 }
20956                                                                 case 'medium': {
20957                                                                         $dom[$key]['fontsize'] = $dom[0]['fontsize'];
20958                                                                         break;
20959                                                                 }
20960                                                                 case 'large': {
20961                                                                         $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 2;
20962                                                                         break;
20963                                                                 }
20964                                                                 case 'x-large': {
20965                                                                         $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 4;
20966                                                                         break;
20967                                                                 }
20968                                                                 case 'xx-large': {
20969                                                                         $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 6;
20970                                                                         break;
20971                                                                 }
20972                                                                 // relative-size
20973                                                                 case 'smaller': {
20974                                                                         $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] - 3;
20975                                                                         break;
20976                                                                 }
20977                                                                 case 'larger': {
20978                                                                         $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] + 3;
20979                                                                         break;
20980                                                                 }
20981                                                                 default: {
20982                                                                         $dom[$key]['fontsize'] = $this->getHTMLUnitToUnits($fsize, $dom[$parentkey]['fontsize'], 'pt', true);
20983                                                                 }
20984                                                         }
20985                                                 }
20986                                                 // font-stretch
20987                                                 if (isset($dom[$key]['style']['font-stretch'])) {
20988                                                         $dom[$key]['font-stretch'] = $this->getCSSFontStretching($dom[$key]['style']['font-stretch'], $dom[$parentkey]['font-stretch']);
20989                                                 }
20990                                                 // letter-spacing
20991                                                 if (isset($dom[$key]['style']['letter-spacing'])) {
20992                                                         $dom[$key]['letter-spacing'] = $this->getCSSFontSpacing($dom[$key]['style']['letter-spacing'], $dom[$parentkey]['letter-spacing']);
20993                                                 }
20994                                                 // line-height
20995                                                 if (isset($dom[$key]['style']['line-height'])) {
20996                                                         $lineheight = trim($dom[$key]['style']['line-height']);
20997                                                         switch ($lineheight) {
20998                                                                 // A normal line height. This is default
20999                                                                 case 'normal': {
21000                                                                         $dom[$key]['line-height'] = $dom[0]['line-height'];
21001                                                                         break;
21002                                                                 }
21003                                                                 default: {
21004                                                                         if (is_numeric($lineheight)) {
21005                                                                                 $lineheight = $lineheight * 100;
21006                                                                         }
21007                                                                         $dom[$key]['line-height'] = $this->getHTMLUnitToUnits($lineheight, 1, '%', true);
21008                                                                 }
21009                                                         }
21010                                                 }
21011                                                 // font style
21012                                                 if (isset($dom[$key]['style']['font-weight'])) {
21013                                                         if (strtolower($dom[$key]['style']['font-weight']{0}) == 'n') {
21014                                                                 if (strpos($dom[$key]['fontstyle'], 'B') !== false) {
21015                                                                         $dom[$key]['fontstyle'] = str_replace('B', '', $dom[$key]['fontstyle']);
21016                                                                 }
21017                                                         } elseif (strtolower($dom[$key]['style']['font-weight']{0}) == 'b') {
21018                                                                 $dom[$key]['fontstyle'] .= 'B';
21019                                                         }
21020                                                 }
21021                                                 if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style']{0}) == 'i')) {
21022                                                         $dom[$key]['fontstyle'] .= 'I';
21023                                                 }
21024                                                 // font color
21025                                                 if (isset($dom[$key]['style']['color']) AND (!$this->empty_string($dom[$key]['style']['color']))) {
21026                                                         $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['color']);
21027                                                 } elseif ($dom[$key]['value'] == 'a') {
21028                                                         $dom[$key]['fgcolor'] = $this->htmlLinkColorArray;
21029                                                 }
21030                                                 // background color
21031                                                 if (isset($dom[$key]['style']['background-color']) AND (!$this->empty_string($dom[$key]['style']['background-color']))) {
21032                                                         $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['background-color']);
21033                                                 }
21034                                                 // text-decoration
21035                                                 if (isset($dom[$key]['style']['text-decoration'])) {
21036                                                         $decors = explode(' ', strtolower($dom[$key]['style']['text-decoration']));
21037                                                         foreach ($decors as $dec) {
21038                                                                 $dec = trim($dec);
21039                                                                 if (!$this->empty_string($dec)) {
21040                                                                         if ($dec{0} == 'u') {
21041                                                                                 // underline
21042                                                                                 $dom[$key]['fontstyle'] .= 'U';
21043                                                                         } elseif ($dec{0} == 'l') {
21044                                                                                 // line-trough
21045                                                                                 $dom[$key]['fontstyle'] .= 'D';
21046                                                                         } elseif ($dec{0} == 'o') {
21047                                                                                 // overline
21048                                                                                 $dom[$key]['fontstyle'] .= 'O';
21049                                                                         }
21050                                                                 }
21051                                                         }
21052                                                 } elseif ($dom[$key]['value'] == 'a') {
21053                                                         $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle;
21054                                                 }
21055                                                 // check for width attribute
21056                                                 if (isset($dom[$key]['style']['width'])) {
21057                                                         $dom[$key]['width'] = $dom[$key]['style']['width'];
21058                                                 }
21059                                                 // check for height attribute
21060                                                 if (isset($dom[$key]['style']['height'])) {
21061                                                         $dom[$key]['height'] = $dom[$key]['style']['height'];
21062                                                 }
21063                                                 // check for text alignment
21064                                                 if (isset($dom[$key]['style']['text-align'])) {
21065                                                         $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align']{0});
21066                                                 }
21067                                                 // check for CSS border properties
21068                                                 if (isset($dom[$key]['style']['border'])) {
21069                                                         $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border']);
21070                                                         if (!empty($borderstyle)) {
21071                                                                 $dom[$key]['border']['LTRB'] = $borderstyle;
21072                                                         }
21073                                                 }
21074                                                 if (isset($dom[$key]['style']['border-color'])) {
21075                                                         $brd_colors = preg_split('/[\s]+/', trim($dom[$key]['style']['border-color']));
21076                                                         if (isset($brd_colors[3])) {
21077                                                                 $dom[$key]['border']['L']['color'] = $this->convertHTMLColorToDec($brd_colors[3]);
21078                                                         }
21079                                                         if (isset($brd_colors[1])) {
21080                                                                 $dom[$key]['border']['R']['color'] = $this->convertHTMLColorToDec($brd_colors[1]);
21081                                                         }
21082                                                         if (isset($brd_colors[0])) {
21083                                                                 $dom[$key]['border']['T']['color'] = $this->convertHTMLColorToDec($brd_colors[0]);
21084                                                         }
21085                                                         if (isset($brd_colors[2])) {
21086                                                                 $dom[$key]['border']['B']['color'] = $this->convertHTMLColorToDec($brd_colors[2]);
21087                                                         }
21088                                                 }
21089                                                 if (isset($dom[$key]['style']['border-width'])) {
21090                                                         $brd_widths = preg_split('/[\s]+/', trim($dom[$key]['style']['border-width']));
21091                                                         if (isset($brd_widths[3])) {
21092                                                                 $dom[$key]['border']['L']['width'] = $this->getCSSBorderWidth($brd_widths[3]);
21093                                                         }
21094                                                         if (isset($brd_widths[1])) {
21095                                                                 $dom[$key]['border']['R']['width'] = $this->getCSSBorderWidth($brd_widths[1]);
21096                                                         }
21097                                                         if (isset($brd_widths[0])) {
21098                                                                 $dom[$key]['border']['T']['width'] = $this->getCSSBorderWidth($brd_widths[0]);
21099                                                         }
21100                                                         if (isset($brd_widths[2])) {
21101                                                                 $dom[$key]['border']['B']['width'] = $this->getCSSBorderWidth($brd_widths[2]);
21102                                                         }
21103                                                 }
21104                                                 if (isset($dom[$key]['style']['border-style'])) {
21105                                                         $brd_styles = preg_split('/[\s]+/', trim($dom[$key]['style']['border-style']));
21106                                                         if (isset($brd_styles[3])) {
21107                                                                 $dom[$key]['border']['L']['cap'] = 'square';
21108                                                                 $dom[$key]['border']['L']['join'] = 'miter';
21109                                                                 $dom[$key]['border']['L']['dash'] = $this->getCSSBorderDashStyle($brd_styles[3]);
21110                                                                 if ($dom[$key]['border']['L']['dash'] < 0) {
21111                                                                         $dom[$key]['border']['L'] = array();
21112                                                                 }
21113                                                         }
21114                                                         if (isset($brd_styles[1])) {
21115                                                                 $dom[$key]['border']['R']['cap'] = 'square';
21116                                                                 $dom[$key]['border']['R']['join'] = 'miter';
21117                                                                 $dom[$key]['border']['R']['dash'] = $this->getCSSBorderDashStyle($brd_styles[1]);
21118                                                                 if ($dom[$key]['border']['R']['dash'] < 0) {
21119                                                                         $dom[$key]['border']['R'] = array();
21120                                                                 }
21121                                                         }
21122                                                         if (isset($brd_styles[0])) {
21123                                                                 $dom[$key]['border']['T']['cap'] = 'square';
21124                                                                 $dom[$key]['border']['T']['join'] = 'miter';
21125                                                                 $dom[$key]['border']['T']['dash'] = $this->getCSSBorderDashStyle($brd_styles[0]);
21126                                                                 if ($dom[$key]['border']['T']['dash'] < 0) {
21127                                                                         $dom[$key]['border']['T'] = array();
21128                                                                 }
21129                                                         }
21130                                                         if (isset($brd_styles[2])) {
21131                                                                 $dom[$key]['border']['B']['cap'] = 'square';
21132                                                                 $dom[$key]['border']['B']['join'] = 'miter';
21133                                                                 $dom[$key]['border']['B']['dash'] = $this->getCSSBorderDashStyle($brd_styles[2]);
21134                                                                 if ($dom[$key]['border']['B']['dash'] < 0) {
21135                                                                         $dom[$key]['border']['B'] = array();
21136                                                                 }
21137                                                         }
21138                                                 }
21139                                                 $cellside = array('L' => 'left', 'R' => 'right', 'T' => 'top', 'B' => 'bottom');
21140                                                 foreach ($cellside as $bsk => $bsv) {
21141                                                         if (isset($dom[$key]['style']['border-'.$bsv])) {
21142                                                                 $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border-'.$bsv]);
21143                                                                 if (!empty($borderstyle)) {
21144                                                                         $dom[$key]['border'][$bsk] = $borderstyle;
21145                                                                 }
21146                                                         }
21147                                                         if (isset($dom[$key]['style']['border-'.$bsv.'-color'])) {
21148                                                                 $dom[$key]['border'][$bsk]['color'] = $this->convertHTMLColorToDec($dom[$key]['style']['border-'.$bsv.'-color']);
21149                                                         }
21150                                                         if (isset($dom[$key]['style']['border-'.$bsv.'-width'])) {
21151                                                                 $dom[$key]['border'][$bsk]['width'] = $this->getCSSBorderWidth($dom[$key]['style']['border-'.$bsv.'-width']);
21152                                                         }
21153                                                         if (isset($dom[$key]['style']['border-'.$bsv.'-style'])) {
21154                                                                 $dom[$key]['border'][$bsk]['dash'] = $this->getCSSBorderDashStyle($dom[$key]['style']['border-'.$bsv.'-style']);
21155                                                                 if ($dom[$key]['border'][$bsk]['dash'] < 0) {
21156                                                                         $dom[$key]['border'][$bsk] = array();
21157                                                                 }
21158                                                         }
21159                                                 }
21160                                                 // check for CSS padding properties
21161                                                 if (isset($dom[$key]['style']['padding'])) {
21162                                                         $dom[$key]['padding'] = $this->getCSSPadding($dom[$key]['style']['padding']);
21163                                                 } else {
21164                                                         $dom[$key]['padding'] = $this->cell_padding;
21165                                                 }
21166                                                 foreach ($cellside as $psk => $psv) {
21167                                                         if (isset($dom[$key]['style']['padding-'.$psv])) {
21168                                                                 $dom[$key]['padding'][$psk] = $this->getHTMLUnitToUnits($dom[$key]['style']['padding-'.$psv], 0, 'px', false);
21169                                                         }
21170                                                 }
21171                                                 // check for CSS margin properties
21172                                                 if (isset($dom[$key]['style']['margin'])) {
21173                                                         $dom[$key]['margin'] = $this->getCSSMargin($dom[$key]['style']['margin']);
21174                                                 } else {
21175                                                         $dom[$key]['margin'] = $this->cell_margin;
21176                                                 }
21177                                                 foreach ($cellside as $psk => $psv) {
21178                                                         if (isset($dom[$key]['style']['margin-'.$psv])) {
21179                                                                 $dom[$key]['margin'][$psk] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $dom[$key]['style']['margin-'.$psv]), 0, 'px', false);
21180                                                         }
21181                                                 }
21182                                                 // check for CSS border-spacing properties
21183                                                 if (isset($dom[$key]['style']['border-spacing'])) {
21184                                                         $dom[$key]['border-spacing'] = $this->getCSSBorderMargin($dom[$key]['style']['border-spacing']);
21185                                                 }
21186                                                 // page-break-inside
21187                                                 if (isset($dom[$key]['style']['page-break-inside']) AND ($dom[$key]['style']['page-break-inside'] == 'avoid')) {
21188                                                         $dom[$key]['attribute']['nobr'] = 'true';
21189                                                 }
21190                                                 // page-break-before
21191                                                 if (isset($dom[$key]['style']['page-break-before'])) {
21192                                                         if ($dom[$key]['style']['page-break-before'] == 'always') {
21193                                                                 $dom[$key]['attribute']['pagebreak'] = 'true';
21194                                                         } elseif ($dom[$key]['style']['page-break-before'] == 'left') {
21195                                                                 $dom[$key]['attribute']['pagebreak'] = 'left';
21196                                                         } elseif ($dom[$key]['style']['page-break-before'] == 'right') {
21197                                                                 $dom[$key]['attribute']['pagebreak'] = 'right';
21198                                                         }
21199                                                 }
21200                                                 // page-break-after
21201                                                 if (isset($dom[$key]['style']['page-break-after'])) {
21202                                                         if ($dom[$key]['style']['page-break-after'] == 'always') {
21203                                                                 $dom[$key]['attribute']['pagebreakafter'] = 'true';
21204                                                         } elseif ($dom[$key]['style']['page-break-after'] == 'left') {
21205                                                                 $dom[$key]['attribute']['pagebreakafter'] = 'left';
21206                                                         } elseif ($dom[$key]['style']['page-break-after'] == 'right') {
21207                                                                 $dom[$key]['attribute']['pagebreakafter'] = 'right';
21208                                                         }
21209                                                 }
21210                                         }
21211                                         if (isset($dom[$key]['attribute']['display'])) {
21212                                                 $dom[$key]['hide'] = (trim(strtolower($dom[$key]['attribute']['display'])) == 'none');
21213                                         }
21214                                         if (isset($dom[$key]['attribute']['border']) AND ($dom[$key]['attribute']['border'] != 0)) {
21215                                                 $borderstyle = $this->getCSSBorderStyle($dom[$key]['attribute']['border'].' solid black');
21216                                                 if (!empty($borderstyle)) {
21217                                                         $dom[$key]['border']['LTRB'] = $borderstyle;
21218                                                 }
21219                                         }
21220                                         // check for font tag
21221                                         if ($dom[$key]['value'] == 'font') {
21222                                                 // font family
21223                                                 if (isset($dom[$key]['attribute']['face'])) {
21224                                                         $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['attribute']['face']);
21225                                                 }
21226                                                 // font size
21227                                                 if (isset($dom[$key]['attribute']['size'])) {
21228                                                         if ($key > 0) {
21229                                                                 if ($dom[$key]['attribute']['size']{0} == '+') {
21230                                                                         $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1));
21231                                                                 } elseif ($dom[$key]['attribute']['size']{0} == '-') {
21232                                                                         $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1));
21233                                                                 } else {
21234                                                                         $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
21235                                                                 }
21236                                                         } else {
21237                                                                 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
21238                                                         }
21239                                                 }
21240                                         }
21241                                         // force natural alignment for lists
21242                                         if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl'))
21243                                                 AND (!isset($dom[$key]['align']) OR $this->empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) {
21244                                                 if ($this->rtl) {
21245                                                         $dom[$key]['align'] = 'R';
21246                                                 } else {
21247                                                         $dom[$key]['align'] = 'L';
21248                                                 }
21249                                         }
21250                                         if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) {
21251                                                 if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
21252                                                         $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO;
21253                                                 }
21254                                         }
21255                                         if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) {
21256                                                 $dom[$key]['fontstyle'] .= 'B';
21257                                         }
21258                                         if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) {
21259                                                 $dom[$key]['fontstyle'] .= 'I';
21260                                         }
21261                                         if ($dom[$key]['value'] == 'u') {
21262                                                 $dom[$key]['fontstyle'] .= 'U';
21263                                         }
21264                                         if (($dom[$key]['value'] == 'del') OR ($dom[$key]['value'] == 's') OR ($dom[$key]['value'] == 'strike')) {
21265                                                 $dom[$key]['fontstyle'] .= 'D';
21266                                         }
21267                                         if (!isset($dom[$key]['style']['text-decoration']) AND ($dom[$key]['value'] == 'a')) {
21268                                                 $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle;
21269                                         }
21270                                         if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) {
21271                                                 $dom[$key]['fontname'] = $this->default_monospaced_font;
21272                                         }
21273                                         if (($dom[$key]['value']{0} == 'h') AND (intval($dom[$key]['value']{1}) > 0) AND (intval($dom[$key]['value']{1}) < 7)) {
21274                                                 // headings h1, h2, h3, h4, h5, h6
21275                                                 if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
21276                                                         $headsize = (4 - intval($dom[$key]['value']{1})) * 2;
21277                                                         $dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize;
21278                                                 }
21279                                                 if (!isset($dom[$key]['style']['font-weight'])) {
21280                                                         $dom[$key]['fontstyle'] .= 'B';
21281                                                 }
21282                                         }
21283                                         if (($dom[$key]['value'] == 'table')) {
21284                                                 $dom[$key]['rows'] = 0; // number of rows
21285                                                 $dom[$key]['trids'] = array(); // IDs of TR elements
21286                                                 $dom[$key]['thead'] = ''; // table header rows
21287                                         }
21288                                         if (($dom[$key]['value'] == 'tr')) {
21289                                                 $dom[$key]['cols'] = 0;
21290                                                 if ($thead) {
21291                                                         $dom[$key]['thead'] = true;
21292                                                         // rows on thead block are printed as a separate table
21293                                                 } else {
21294                                                         $dom[$key]['thead'] = false;
21295                                                         // store the number of rows on table element
21296                                                         ++$dom[($dom[$key]['parent'])]['rows'];
21297                                                         // store the TR elements IDs on table element
21298                                                         array_push($dom[($dom[$key]['parent'])]['trids'], $key);
21299                                                 }
21300                                         }
21301                                         if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) {
21302                                                 if (isset($dom[$key]['attribute']['colspan'])) {
21303                                                         $colspan = intval($dom[$key]['attribute']['colspan']);
21304                                                 } else {
21305                                                         $colspan = 1;
21306                                                 }
21307                                                 $dom[$key]['attribute']['colspan'] = $colspan;
21308                                                 $dom[($dom[$key]['parent'])]['cols'] += $colspan;
21309                                         }
21310                                         // text direction
21311                                         if (isset($dom[$key]['attribute']['dir'])) {
21312                                                 $dom[$key]['dir'] = $dom[$key]['attribute']['dir'];
21313                                         }
21314                                         // set foreground color attribute
21315                                         if (isset($dom[$key]['attribute']['color']) AND (!$this->empty_string($dom[$key]['attribute']['color']))) {
21316                                                 $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['color']);
21317                                         } elseif (!isset($dom[$key]['style']['color']) AND ($dom[$key]['value'] == 'a')) {
21318                                                 $dom[$key]['fgcolor'] = $this->htmlLinkColorArray;
21319                                         }
21320                                         // set background color attribute
21321                                         if (isset($dom[$key]['attribute']['bgcolor']) AND (!$this->empty_string($dom[$key]['attribute']['bgcolor']))) {
21322                                                 $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['bgcolor']);
21323                                         }
21324                                         // set stroke color attribute
21325                                         if (isset($dom[$key]['attribute']['strokecolor']) AND (!$this->empty_string($dom[$key]['attribute']['strokecolor']))) {
21326                                                 $dom[$key]['strokecolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['strokecolor']);
21327                                         }
21328                                         // check for width attribute
21329                                         if (isset($dom[$key]['attribute']['width'])) {
21330                                                 $dom[$key]['width'] = $dom[$key]['attribute']['width'];
21331                                         }
21332                                         // check for height attribute
21333                                         if (isset($dom[$key]['attribute']['height'])) {
21334                                                 $dom[$key]['height'] = $dom[$key]['attribute']['height'];
21335                                         }
21336                                         // check for text alignment
21337                                         if (isset($dom[$key]['attribute']['align']) AND (!$this->empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) {
21338                                                 $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align']{0});
21339                                         }
21340                                         // check for text rendering mode (the following attributes do not exist in HTML)
21341                                         if (isset($dom[$key]['attribute']['stroke'])) {
21342                                                 // font stroke width
21343                                                 $dom[$key]['stroke'] = $this->getHTMLUnitToUnits($dom[$key]['attribute']['stroke'], $dom[$key]['fontsize'], 'pt', true);
21344                                         }
21345                                         if (isset($dom[$key]['attribute']['fill'])) {
21346                                                 // font fill
21347                                                 if ($dom[$key]['attribute']['fill'] == 'true') {
21348                                                         $dom[$key]['fill'] = true;
21349                                                 } else {
21350                                                         $dom[$key]['fill'] = false;
21351                                                 }
21352                                         }
21353                                         if (isset($dom[$key]['attribute']['clip'])) {
21354                                                 // clipping mode
21355                                                 if ($dom[$key]['attribute']['clip'] == 'true') {
21356                                                         $dom[$key]['clip'] = true;
21357                                                 } else {
21358                                                         $dom[$key]['clip'] = false;
21359                                                 }
21360                                         }
21361                                 } // end opening tag
21362                         } else {
21363                                 // text
21364                                 $dom[$key]['tag'] = false;
21365                                 $dom[$key]['block'] = false;
21366                                 //$element = str_replace('&nbsp;', $this->unichr(160), $element);
21367                                 $dom[$key]['value'] = stripslashes($this->unhtmlentities($element));
21368                                 $dom[$key]['parent'] = end($level);
21369                                 $dom[$key]['dir'] = $dom[$dom[$key]['parent']]['dir'];
21370                         }
21371                         ++$elkey;
21372                         ++$key;
21373                 }
21374                 return $dom;
21375         }
21376 
21384         protected function getSpaceString() {
21385                 $spacestr = chr(32);
21386                 if ($this->isUnicodeFont()) {
21387                         $spacestr = chr(0).chr(32);
21388                 }
21389                 return $spacestr;
21390         }
21391 
21411         public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=false, $reseth=true, $align='', $autopadding=true) {
21412                 return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true, $autopadding, 0, 'T', false);
21413         }
21414 
21427         public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') {
21428                 $gvars = $this->getGraphicVars();
21429                 // store current values
21430                 $prev_cell_margin = $this->cell_margin;
21431                 $prev_cell_padding = $this->cell_padding;
21432                 $prevPage = $this->page;
21433                 $prevlMargin = $this->lMargin;
21434                 $prevrMargin = $this->rMargin;
21435                 $curfontname = $this->FontFamily;
21436                 $curfontstyle = $this->FontStyle;
21437                 $curfontsize = $this->FontSizePt;
21438                 $curfontascent = $this->getFontAscent($curfontname, $curfontstyle, $curfontsize);
21439                 $curfontdescent = $this->getFontDescent($curfontname, $curfontstyle, $curfontsize);
21440                 $curfontstretcing = $this->font_stretching;
21441                 $curfontkerning = $this->font_spacing;
21442                 $this->newline = true;
21443                 $newline = true;
21444                 $startlinepage = $this->page;
21445                 $minstartliney = $this->y;
21446                 $maxbottomliney = 0;
21447                 $startlinex = $this->x;
21448                 $startliney = $this->y;
21449                 $yshift = 0;
21450                 $loop = 0;
21451                 $curpos = 0;
21452                 $this_method_vars = array();
21453                 $undo = false;
21454                 $fontaligned = false;
21455                 $reverse_dir = false; // true when the text direction is reversed
21456                 $this->premode = false;
21457                 if ($this->inxobj) {
21458                         // we are inside an XObject template
21459                         $pask = count($this->xobjects[$this->xobjid]['annotations']);
21460                 } elseif (isset($this->PageAnnots[$this->page])) {
21461                         $pask = count($this->PageAnnots[$this->page]);
21462                 } else {
21463                         $pask = 0;
21464                 }
21465                 if ($this->inxobj) {
21466                         // we are inside an XObject template
21467                         $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']);
21468                 } elseif (!$this->InFooter) {
21469                         if (isset($this->footerlen[$this->page])) {
21470                                 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
21471                         } else {
21472                                 $this->footerpos[$this->page] = $this->pagelen[$this->page];
21473                         }
21474                         $startlinepos = $this->footerpos[$this->page];
21475                 } else {
21476                         // we are inside the footer
21477                         $startlinepos = $this->pagelen[$this->page];
21478                 }
21479                 $lalign = $align;
21480                 $plalign = $align;
21481                 if ($this->rtl) {
21482                         $w = $this->x - $this->lMargin;
21483                 } else {
21484                         $w = $this->w - $this->rMargin - $this->x;
21485                 }
21486                 $w -= ($this->cell_padding['L'] + $this->cell_padding['R']);
21487                 if ($cell) {
21488                         if ($this->rtl) {
21489                                 $this->x -= $this->cell_padding['R'];
21490                                 $this->lMargin += $this->cell_padding['R'];
21491                         } else {
21492                                 $this->x += $this->cell_padding['L'];
21493                                 $this->rMargin += $this->cell_padding['L'];
21494                         }
21495                 }
21496                 if ($this->customlistindent >= 0) {
21497                         $this->listindent = $this->customlistindent;
21498                 } else {
21499                         $this->listindent = $this->GetStringWidth('000000');
21500                 }
21501                 $this->listindentlevel = 0;
21502                 // save previous states
21503                 $prev_cell_height_ratio = $this->cell_height_ratio;
21504                 $prev_listnum = $this->listnum;
21505                 $prev_listordered = $this->listordered;
21506                 $prev_listcount = $this->listcount;
21507                 $prev_lispacer = $this->lispacer;
21508                 $this->listnum = 0;
21509                 $this->listordered = array();
21510                 $this->listcount = array();
21511                 $this->lispacer = '';
21512                 if (($this->empty_string($this->lasth)) OR ($reseth)) {
21513                         // reset row height
21514                         $this->resetLastH();
21515                 }
21516                 $dom = $this->getHtmlDomArray($html);
21517                 $maxel = count($dom);
21518                 $key = 0;
21519                 $hidden_node_key = -1;
21520                 while ($key < $maxel) {
21521                         if ($dom[$key]['tag']) {
21522                                 if ($dom[$key]['opening']) {
21523                                         if (($hidden_node_key <= 0) AND $dom[$key]['hide']) {
21524                                                 // store the node key
21525                                                 $hidden_node_key = $key;
21526                                         }
21527                                 } elseif (($hidden_node_key > 0) AND ($dom[$key]['parent'] == $hidden_node_key)) {
21528                                         // we have reached the closing tag of the hidden node
21529                                         $hidden_node_key = 0;
21530                                 }
21531                         }
21532                         if ($hidden_node_key >= 0) {
21533                                 // skip this node
21534                                 ++$key;
21535                                 if ($hidden_node_key == 0) {
21536                                         // reset hidden mode
21537                                         $hidden_node_key = -1;
21538                                 }
21539                                 continue;
21540                         }
21541                         if ($dom[$key]['tag'] AND isset($dom[$key]['attribute']['pagebreak'])) {
21542                                 // check for pagebreak
21543                                 if (($dom[$key]['attribute']['pagebreak'] == 'true') OR ($dom[$key]['attribute']['pagebreak'] == 'left') OR ($dom[$key]['attribute']['pagebreak'] == 'right')) {
21544                                         // add a page (or trig AcceptPageBreak() for multicolumn mode)
21545                                         $this->checkPageBreak($this->PageBreakTrigger + 1);
21546                                 }
21547                                 if ((($dom[$key]['attribute']['pagebreak'] == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
21548                                         OR (($dom[$key]['attribute']['pagebreak'] == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
21549                                         // add a page (or trig AcceptPageBreak() for multicolumn mode)
21550                                         $this->checkPageBreak($this->PageBreakTrigger + 1);
21551                                 }
21552                         }
21553                         if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND isset($dom[$key]['attribute']['nobr']) AND ($dom[$key]['attribute']['nobr'] == 'true')) {
21554                                 if (isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) {
21555                                         $dom[$key]['attribute']['nobr'] = false;
21556                                 } else {
21557                                         // store current object
21558                                         $this->startTransaction();
21559                                         // save this method vars
21560                                         $this_method_vars['html'] = $html;
21561                                         $this_method_vars['ln'] = $ln;
21562                                         $this_method_vars['fill'] = $fill;
21563                                         $this_method_vars['reseth'] = $reseth;
21564                                         $this_method_vars['cell'] = $cell;
21565                                         $this_method_vars['align'] = $align;
21566                                         $this_method_vars['gvars'] = $gvars;
21567                                         $this_method_vars['prevPage'] = $prevPage;
21568                                         $this_method_vars['prev_cell_margin'] = $prev_cell_margin;
21569                                         $this_method_vars['prev_cell_padding'] = $prev_cell_padding;
21570                                         $this_method_vars['prevlMargin'] = $prevlMargin;
21571                                         $this_method_vars['prevrMargin'] = $prevrMargin;
21572                                         $this_method_vars['curfontname'] = $curfontname;
21573                                         $this_method_vars['curfontstyle'] = $curfontstyle;
21574                                         $this_method_vars['curfontsize'] = $curfontsize;
21575                                         $this_method_vars['curfontascent'] = $curfontascent;
21576                                         $this_method_vars['curfontdescent'] = $curfontdescent;
21577                                         $this_method_vars['curfontstretcing'] = $curfontstretcing;
21578                                         $this_method_vars['curfontkerning'] = $curfontkerning;
21579                                         $this_method_vars['minstartliney'] = $minstartliney;
21580                                         $this_method_vars['maxbottomliney'] = $maxbottomliney;
21581                                         $this_method_vars['yshift'] = $yshift;
21582                                         $this_method_vars['startlinepage'] = $startlinepage;
21583                                         $this_method_vars['startlinepos'] = $startlinepos;
21584                                         $this_method_vars['startlinex'] = $startlinex;
21585                                         $this_method_vars['startliney'] = $startliney;
21586                                         $this_method_vars['newline'] = $newline;
21587                                         $this_method_vars['loop'] = $loop;
21588                                         $this_method_vars['curpos'] = $curpos;
21589                                         $this_method_vars['pask'] = $pask;
21590                                         $this_method_vars['lalign'] = $lalign;
21591                                         $this_method_vars['plalign'] = $plalign;
21592                                         $this_method_vars['w'] = $w;
21593                                         $this_method_vars['prev_cell_height_ratio'] = $prev_cell_height_ratio;
21594                                         $this_method_vars['prev_listnum'] = $prev_listnum;
21595                                         $this_method_vars['prev_listordered'] = $prev_listordered;
21596                                         $this_method_vars['prev_listcount'] = $prev_listcount;
21597                                         $this_method_vars['prev_lispacer'] = $prev_lispacer;
21598                                         $this_method_vars['fontaligned'] = $fontaligned;
21599                                         $this_method_vars['key'] = $key;
21600                                         $this_method_vars['dom'] = $dom;
21601                                 }
21602                         }
21603                         // print THEAD block
21604                         if (($dom[$key]['value'] == 'tr') AND isset($dom[$key]['thead']) AND $dom[$key]['thead']) {
21605                                 if (isset($dom[$key]['parent']) AND isset($dom[$dom[$key]['parent']]['thead']) AND !$this->empty_string($dom[$dom[$key]['parent']]['thead'])) {
21606                                         $this->inthead = true;
21607                                         // print table header (thead)
21608                                         $this->writeHTML($this->thead, false, false, false, false, '');
21609                                         // check if we are on a new page or on a new column
21610                                         if (($this->y < $this->start_transaction_y) OR ($this->checkPageBreak($this->lasth, '', false))) {
21611                                                 // we are on a new page or on a new column and the total object height is less than the available vertical space.
21612                                                 // restore previous object
21613                                                 $this->rollbackTransaction(true);
21614                                                 // restore previous values
21615                                                 foreach ($this_method_vars as $vkey => $vval) {
21616                                                         $$vkey = $vval;
21617                                                 }
21618                                                 // disable table header
21619                                                 $tmp_thead = $this->thead;
21620                                                 $this->thead = '';
21621                                                 // add a page (or trig AcceptPageBreak() for multicolumn mode)
21622                                                 $pre_y = $this->y;
21623                                                 if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) {
21624                                                         // fix for multicolumn mode
21625                                                         $startliney = $this->y;
21626                                                 }
21627                                                 $this->start_transaction_page = $this->page;
21628                                                 $this->start_transaction_y = $this->y;
21629                                                 // restore table header
21630                                                 $this->thead = $tmp_thead;
21631                                                 // fix table border properties
21632                                                 if (isset($dom[$dom[$key]['parent']]['attribute']['cellspacing'])) {
21633                                                         $tmp_cellspacing = $this->getHTMLUnitToUnits($dom[$dom[$key]['parent']]['attribute']['cellspacing'], 1, 'px');
21634                                                 } elseif (isset($dom[$dom[$key]['parent']]['border-spacing'])) {
21635                                                         $tmp_cellspacing = $dom[$dom[$key]['parent']]['border-spacing']['V'];
21636                                                 } else {
21637                                                         $tmp_cellspacing = 0;
21638                                                 }
21639                                                 $dom[$dom[$key]['parent']]['borderposition']['page'] = $this->page;
21640                                                 $dom[$dom[$key]['parent']]['borderposition']['column'] = $this->current_column;
21641                                                 $dom[$dom[$key]['parent']]['borderposition']['y'] = $this->y + $tmp_cellspacing;
21642                                                 $xoffset = ($this->x - $dom[$dom[$key]['parent']]['borderposition']['x']);
21643                                                 $dom[$dom[$key]['parent']]['borderposition']['x'] += $xoffset;
21644                                                 $dom[$dom[$key]['parent']]['borderposition']['xmax'] += $xoffset;
21645                                                 // print table header (thead)
21646                                                 $this->writeHTML($this->thead, false, false, false, false, '');
21647                                         }
21648                                 }
21649                                 // move $key index forward to skip THEAD block
21650                                 while ( ($key < $maxel) AND (!(
21651                                         ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'tr') AND (!isset($dom[$key]['thead']) OR !$dom[$key]['thead']))
21652                                         OR ($dom[$key]['tag'] AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == 'table'))) )) {
21653                                         ++$key;
21654                                 }
21655                         }
21656                         if ($dom[$key]['tag'] OR ($key == 0)) {
21657                                 if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) {
21658                                         $dom[$key]['align'] = ($this->rtl) ? 'R' : 'L';
21659                                 }
21660                                 // vertically align image in line
21661                                 if ((!$this->newline) AND ($dom[$key]['value'] == 'img') AND (isset($dom[$key]['height'])) AND ($dom[$key]['height'] > 0)) {
21662                                         // get image height
21663                                         $imgh = $this->getHTMLUnitToUnits($dom[$key]['height'], $this->lasth, 'px');
21664                                         $autolinebreak = false;
21665                                         if (isset($dom[$key]['width']) AND ($dom[$key]['width'] > 0)) {
21666                                                 $imgw = $this->getHTMLUnitToUnits($dom[$key]['width'], 1, 'px', false);
21667                                                 if (($imgw <= ($this->w - $this->lMargin - $this->rMargin - $this->cell_padding['L'] - $this->cell_padding['R']))
21668                                                         AND ((($this->rtl) AND (($this->x - $imgw) < ($this->lMargin + $this->cell_padding['L'])))
21669                                                         OR ((!$this->rtl) AND (($this->x + $imgw) > ($this->w - $this->rMargin - $this->cell_padding['R']))))) {
21670                                                         // add automatic line break
21671                                                         $autolinebreak = true;
21672                                                         $this->Ln('', $cell);
21673                                                         if ((!$dom[($key-1)]['tag']) AND ($dom[($key-1)]['value'] == ' ')) {
21674                                                                 // go back to evaluate this line break
21675                                                                 --$key;
21676                                                         }
21677                                                 }
21678                                         }
21679                                         if (!$autolinebreak) {
21680                                                 if ($this->inPageBody()) {
21681                                                         $pre_y = $this->y;
21682                                                         // check for page break
21683                                                         if ((!$this->checkPageBreak($imgh)) AND ($this->y < $pre_y)) {
21684                                                                 // fix for multicolumn mode
21685                                                                 $startliney = $this->y;
21686                                                         }
21687                                                 }
21688                                                 if ($this->page > $startlinepage) {
21689                                                         // fix line splitted over two pages
21690                                                         if (isset($this->footerlen[$startlinepage])) {
21691                                                                 $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
21692                                                         }
21693                                                         // line to be moved one page forward
21694                                                         $pagebuff = $this->getPageBuffer($startlinepage);
21695                                                         $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
21696                                                         $tstart = substr($pagebuff, 0, $startlinepos);
21697                                                         $tend = substr($this->getPageBuffer($startlinepage), $curpos);
21698                                                         // remove line from previous page
21699                                                         $this->setPageBuffer($startlinepage, $tstart.''.$tend);
21700                                                         $pagebuff = $this->getPageBuffer($this->page);
21701                                                         $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
21702                                                         $tend = substr($pagebuff, $this->cntmrk[$this->page]);
21703                                                         // add line start to current page
21704                                                         $yshift = ($minstartliney - $this->y);
21705                                                         if ($fontaligned) {
21706                                                                 $yshift += ($curfontsize / $this->k);
21707                                                         }
21708                                                         $try = sprintf('1 0 0 1 0 %.3F cm', ($yshift * $this->k));
21709                                                         $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
21710                                                         // shift the annotations and links
21711                                                         if (isset($this->PageAnnots[$this->page])) {
21712                                                                 $next_pask = count($this->PageAnnots[$this->page]);
21713                                                         } else {
21714                                                                 $next_pask = 0;
21715                                                         }
21716                                                         if (isset($this->PageAnnots[$startlinepage])) {
21717                                                                 foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
21718                                                                         if ($pak >= $pask) {
21719                                                                                 $this->PageAnnots[$this->page][] = $pac;
21720                                                                                 unset($this->PageAnnots[$startlinepage][$pak]);
21721                                                                                 $npak = count($this->PageAnnots[$this->page]) - 1;
21722                                                                                 $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
21723                                                                         }
21724                                                                 }
21725                                                         }
21726                                                         $pask = $next_pask;
21727                                                         $startlinepos = $this->cntmrk[$this->page];
21728                                                         $startlinepage = $this->page;
21729                                                         $startliney = $this->y;
21730                                                         $this->newline = false;
21731                                                 }
21732                                                 $this->y += ((($curfontsize * $this->cell_height_ratio / $this->k) + $curfontascent - $curfontdescent) / 2) - $imgh;
21733                                                 $minstartliney = min($this->y, $minstartliney);
21734                                                 $maxbottomliney = ($startliney + ($this->FontSize * $this->cell_height_ratio));
21735                                         }
21736                                 } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize']) OR isset($dom[$key]['line-height'])) {
21737                                         // account for different font size
21738                                         $pfontname = $curfontname;
21739                                         $pfontstyle = $curfontstyle;
21740                                         $pfontsize = $curfontsize;
21741                                         $fontname = isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname;
21742                                         $fontstyle = isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle;
21743                                         $fontsize = isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize;
21744                                         $fontascent = $this->getFontAscent($fontname, $fontstyle, $fontsize);
21745                                         $fontdescent = $this->getFontDescent($fontname, $fontstyle, $fontsize);
21746                                         if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)
21747                                                 OR ($this->cell_height_ratio != $dom[$key]['line-height'])
21748                                                 OR ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li')) ) {
21749                                                 if (($key < ($maxel - 1)) AND (
21750                                                                 ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li'))
21751                                                                 OR ($this->cell_height_ratio != $dom[$key]['line-height'])
21752                                                                 OR (!$this->newline AND is_numeric($fontsize) AND is_numeric($curfontsize) AND ($fontsize >= 0) AND ($curfontsize >= 0) AND ($fontsize != $curfontsize))
21753                                                         )) {
21754                                                         if ($this->page > $startlinepage) {
21755                                                                 // fix lines splitted over two pages
21756                                                                 if (isset($this->footerlen[$startlinepage])) {
21757                                                                         $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
21758                                                                 }
21759                                                                 // line to be moved one page forward
21760                                                                 $pagebuff = $this->getPageBuffer($startlinepage);
21761                                                                 $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
21762                                                                 $tstart = substr($pagebuff, 0, $startlinepos);
21763                                                                 $tend = substr($this->getPageBuffer($startlinepage), $curpos);
21764                                                                 // remove line start from previous page
21765                                                                 $this->setPageBuffer($startlinepage, $tstart.''.$tend);
21766                                                                 $pagebuff = $this->getPageBuffer($this->page);
21767                                                                 $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
21768                                                                 $tend = substr($pagebuff, $this->cntmrk[$this->page]);
21769                                                                 // add line start to current page
21770                                                                 $yshift = ($minstartliney - $this->y);
21771                                                                 $try = sprintf('1 0 0 1 0 %.3F cm', ($yshift * $this->k));
21772                                                                 $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
21773                                                                 // shift the annotations and links
21774                                                                 if (isset($this->PageAnnots[$this->page])) {
21775                                                                         $next_pask = count($this->PageAnnots[$this->page]);
21776                                                                 } else {
21777                                                                         $next_pask = 0;
21778                                                                 }
21779                                                                 if (isset($this->PageAnnots[$startlinepage])) {
21780                                                                         foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
21781                                                                                 if ($pak >= $pask) {
21782                                                                                         $this->PageAnnots[$this->page][] = $pac;
21783                                                                                         unset($this->PageAnnots[$startlinepage][$pak]);
21784                                                                                         $npak = count($this->PageAnnots[$this->page]) - 1;
21785                                                                                         $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
21786                                                                                 }
21787                                                                         }
21788                                                                 }
21789                                                                 $pask = $next_pask;
21790                                                                 $startlinepos = $this->cntmrk[$this->page];
21791                                                                 $startlinepage = $this->page;
21792                                                                 $startliney = $this->y;
21793                                                         }
21794                                                         if (!isset($dom[$key]['line-height'])) {
21795                                                                 $dom[$key]['line-height'] = $this->cell_height_ratio;
21796                                                         }
21797                                                         if (!$dom[$key]['block']) {
21798                                                                 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']))) {
21799                                                                         $this->y += (((($curfontsize * $this->cell_height_ratio) - ($fontsize * $dom[$key]['line-height'])) / $this->k) + $curfontascent - $fontascent - $curfontdescent + $fontdescent) / 2;
21800                                                                 }
21801                                                                 if (($dom[$key]['value'] != 'sup') AND ($dom[$key]['value'] != 'sub')) {
21802                                                                         $current_line_align_data = array($key, $minstartliney, $maxbottomliney);
21803                                                                         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)))) {
21804                                                                                 $minstartliney = min($this->y, $line_align_data[1]);
21805                                                                                 $maxbottomliney = max(($this->y + (($fontsize * $this->cell_height_ratio) / $this->k)), $line_align_data[2]);
21806                                                                         } else {
21807                                                                                 $minstartliney = min($this->y, $minstartliney);
21808                                                                                 $maxbottomliney = max(($this->y + (($fontsize * $this->cell_height_ratio) / $this->k)), $maxbottomliney);
21809                                                                         }
21810                                                                         $line_align_data = $current_line_align_data;
21811                                                                 }
21812                                                         }
21813                                                         $this->cell_height_ratio = $dom[$key]['line-height'];
21814                                                         $fontaligned = true;
21815                                                 }
21816                                                 $this->SetFont($fontname, $fontstyle, $fontsize);
21817                                                 // reset row height
21818                                                 $this->resetLastH();
21819                                                 $curfontname = $fontname;
21820                                                 $curfontstyle = $fontstyle;
21821                                                 $curfontsize = $fontsize;
21822                                                 $curfontascent = $fontascent;
21823                                                 $curfontdescent = $fontdescent;
21824                                         }
21825                                 }
21826                                 // set text rendering mode
21827                                 $textstroke = isset($dom[$key]['stroke']) ? $dom[$key]['stroke'] : $this->textstrokewidth;
21828                                 $textfill = isset($dom[$key]['fill']) ? $dom[$key]['fill'] : (($this->textrendermode % 2) == 0);
21829                                 $textclip = isset($dom[$key]['clip']) ? $dom[$key]['clip'] : ($this->textrendermode > 3);
21830                                 $this->setTextRenderingMode($textstroke, $textfill, $textclip);
21831                                 if (isset($dom[$key]['font-stretch']) AND ($dom[$key]['font-stretch'] !== false)) {
21832                                         $this->setFontStretching($dom[$key]['font-stretch']);
21833                                 }
21834                                 if (isset($dom[$key]['letter-spacing']) AND ($dom[$key]['letter-spacing'] !== false)) {
21835                                         $this->setFontSpacing($dom[$key]['letter-spacing']);
21836                                 }
21837                                 if (($plalign == 'J') AND $dom[$key]['block']) {
21838                                         $plalign = '';
21839                                 }
21840                                 // get current position on page buffer
21841                                 $curpos = $this->pagelen[$startlinepage];
21842                                 if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) {
21843                                         $this->SetFillColorArray($dom[$key]['bgcolor']);
21844                                         $wfill = true;
21845                                 } else {
21846                                         $wfill = $fill | false;
21847                                 }
21848                                 if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) {
21849                                         $this->SetTextColorArray($dom[$key]['fgcolor']);
21850                                 }
21851                                 if (isset($dom[$key]['strokecolor']) AND ($dom[$key]['strokecolor'] !== false)) {
21852                                         $this->SetDrawColorArray($dom[$key]['strokecolor']);
21853                                 }
21854                                 if (isset($dom[$key]['align'])) {
21855                                         $lalign = $dom[$key]['align'];
21856                                 }
21857                                 if ($this->empty_string($lalign)) {
21858                                         $lalign = $align;
21859                                 }
21860                         }
21861                         // align lines
21862                         if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) {
21863                                 $newline = true;
21864                                 $fontaligned = false;
21865                                 // we are at the beginning of a new line
21866                                 if (isset($startlinex)) {
21867                                         $yshift = ($minstartliney - $startliney);
21868                                         if (($yshift > 0) OR ($this->page > $startlinepage)) {
21869                                                 $yshift = 0;
21870                                         }
21871                                         $t_x = 0;
21872                                         // the last line must be shifted to be aligned as requested
21873                                         $linew = abs($this->endlinex - $startlinex);
21874                                         if ($this->inxobj) {
21875                                                 // we are inside an XObject template
21876                                                 $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos);
21877                                                 if (isset($opentagpos)) {
21878                                                         $midpos = $opentagpos;
21879                                                 } else {
21880                                                         $midpos = 0;
21881                                                 }
21882                                                 if ($midpos > 0) {
21883                                                         $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos));
21884                                                         $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos);
21885                                                 } else {
21886                                                         $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos);
21887                                                         $pend = '';
21888                                                 }
21889                                         } else {
21890                                                 $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
21891                                                 if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
21892                                                         $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
21893                                                         $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
21894                                                 } elseif (isset($opentagpos)) {
21895                                                         $midpos = $opentagpos;
21896                                                 } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
21897                                                         $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
21898                                                         $midpos = $this->footerpos[$startlinepage];
21899                                                 } else {
21900                                                         $midpos = 0;
21901                                                 }
21902                                                 if ($midpos > 0) {
21903                                                         $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
21904                                                         $pend = substr($this->getPageBuffer($startlinepage), $midpos);
21905                                                 } else {
21906                                                         $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
21907                                                         $pend = '';
21908                                                 }
21909                                         }
21910                                         if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) {
21911                                                 // calculate shifting amount
21912                                                 $tw = $w;
21913                                                 if (($plalign == 'J') AND $this->isRTLTextDir() AND ($this->num_columns > 1)) {
21914                                                         $tw += $this->cell_padding['R'];
21915                                                 }
21916                                                 if ($this->lMargin != $prevlMargin) {
21917                                                         $tw += ($prevlMargin - $this->lMargin);
21918                                                 }
21919                                                 if ($this->rMargin != $prevrMargin) {
21920                                                         $tw += ($prevrMargin - $this->rMargin);
21921                                                 }
21922                                                 $one_space_width = $this->GetStringWidth(chr(32));
21923                                                 $no = 0; // number of spaces on a line contained on a single block
21924                                                 if ($this->isRTLTextDir()) { // RTL
21925                                                         // remove left space if exist
21926                                                         $pos1 = $this->revstrpos($pmid, '[(');
21927                                                         if ($pos1 > 0) {
21928                                                                 $pos1 = intval($pos1);
21929                                                                 if ($this->isUnicodeFont()) {
21930                                                                         $pos2 = intval($this->revstrpos($pmid, '[('.chr(0).chr(32)));
21931                                                                         $spacelen = 2;
21932                                                                 } else {
21933                                                                         $pos2 = intval($this->revstrpos($pmid, '[('.chr(32)));
21934                                                                         $spacelen = 1;
21935                                                                 }
21936                                                                 if ($pos1 == $pos2) {
21937                                                                         $pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen));
21938                                                                         if (substr($pmid, $pos1, 4) == '[()]') {
21939                                                                                 $linew -= $one_space_width;
21940                                                                         } elseif ($pos1 == strpos($pmid, '[(')) {
21941                                                                                 $no = 1;
21942                                                                         }
21943                                                                 }
21944                                                         }
21945                                                 } else { // LTR
21946                                                         // remove right space if exist
21947                                                         $pos1 = $this->revstrpos($pmid, ')]');
21948                                                         if ($pos1 > 0) {
21949                                                                 $pos1 = intval($pos1);
21950                                                                 if ($this->isUnicodeFont()) {
21951                                                                         $pos2 = intval($this->revstrpos($pmid, chr(0).chr(32).')]')) + 2;
21952                                                                         $spacelen = 2;
21953                                                                 } else {
21954                                                                         $pos2 = intval($this->revstrpos($pmid, chr(32).')]')) + 1;
21955                                                                         $spacelen = 1;
21956                                                                 }
21957                                                                 if ($pos1 == $pos2) {
21958                                                                         $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
21959                                                                         $linew -= $one_space_width;
21960                                                                 }
21961                                                         }
21962                                                 }
21963                                                 $mdiff = ($tw - $linew);
21964                                                 if ($plalign == 'C') {
21965                                                         if ($this->rtl) {
21966                                                                 $t_x = -($mdiff / 2);
21967                                                         } else {
21968                                                                 $t_x = ($mdiff / 2);
21969                                                         }
21970                                                 } elseif ($plalign == 'R') {
21971                                                         // right alignment on LTR document
21972                                                         $t_x = $mdiff;
21973                                                 } elseif ($plalign == 'L') {
21974                                                         // left alignment on RTL document
21975                                                         $t_x = -$mdiff;
21976                                                 } elseif (($plalign == 'J') AND ($plalign == $lalign)) {
21977                                                         // Justification
21978                                                         if ($this->isRTLTextDir()) {
21979                                                                 // align text on the left
21980                                                                 $t_x = -$mdiff;
21981                                                         }
21982                                                         $ns = 0; // number of spaces
21983                                                         $pmidtemp = $pmid;
21984                                                         // escape special characters
21985                                                         $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
21986                                                         $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
21987                                                         // search spaces
21988                                                         if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER)) {
21989                                                                 $spacestr = $this->getSpaceString();
21990                                                                 $maxkk = count($lnstring[1]) - 1;
21991                                                                 for ($kk=0; $kk <= $maxkk; ++$kk) {
21992                                                                         // restore special characters
21993                                                                         $lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]);
21994                                                                         $lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]);
21995                                                                         // store number of spaces on the strings
21996                                                                         $lnstring[2][$kk] = substr_count($lnstring[1][$kk], $spacestr);
21997                                                                         // count total spaces on line
21998                                                                         $ns += $lnstring[2][$kk];
21999                                                                         $lnstring[3][$kk] = $ns;
22000                                                                 }
22001                                                                 if ($ns == 0) {
22002                                                                         $ns = 1;
22003                                                                 }
22004                                                                 // calculate additional space to add to each existing space
22005                                                                 $spacewidth = ($mdiff / ($ns - $no)) * $this->k;
22006                                                                 $spacewidthu = -1000 * ($mdiff + (($ns + $no) * $one_space_width)) / $ns / $this->FontSize;
22007                                                                 if ($this->font_spacing != 0) {
22008                                                                         // fixed spacing mode
22009                                                                         $osw = -1000 * $this->font_spacing / $this->FontSize;
22010                                                                         $spacewidthu += $osw;
22011                                                                 }
22012                                                                 $nsmax = $ns;
22013                                                                 $ns = 0;
22014                                                                 reset($lnstring);
22015                                                                 $offset = 0;
22016                                                                 $strcount = 0;
22017                                                                 $prev_epsposbeg = 0;
22018                                                                 $textpos = 0;
22019                                                                 if ($this->isRTLTextDir()) {
22020                                                                         $textpos = $this->wPt;
22021                                                                 }
22022                                                                 global $spacew;
22023                                                                 while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE, $offset) == 1) {
22024                                                                         // check if we are inside a string section '[( ... )]'
22025                                                                         $stroffset = strpos($pmid, '[(', $offset);
22026                                                                         if (($stroffset !== false) AND ($stroffset <= $strpiece[2][1])) {
22027                                                                                 // set offset to the end of string section
22028                                                                                 $offset = strpos($pmid, ')]', $stroffset);
22029                                                                                 while (($offset !== false) AND ($pmid{($offset - 1)} == '\\')) {
22030                                                                                         $offset = strpos($pmid, ')]', ($offset + 1));
22031                                                                                 }
22032                                                                                 if ($offset === false) {
22033                                                                                         $this->Error('HTML Justification: malformed PDF code.');
22034                                                                                 }
22035                                                                                 continue;
22036                                                                         }
22037                                                                         if ($this->isRTLTextDir()) {
22038                                                                                 $spacew = ($spacewidth * ($nsmax - $ns));
22039                                                                         } else {
22040                                                                                 $spacew = ($spacewidth * $ns);
22041                                                                         }
22042                                                                         $offset = $strpiece[2][1] + strlen($strpiece[2][0]);
22043                                                                         $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, $offset);
22044                                                                         $epsposend = strpos($pmid, $this->epsmarker.'Q', $offset) + strlen($this->epsmarker.'Q');
22045                                                                         if ((($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend))
22046                                                                                 OR (($epsposbeg === false) AND ($epsposend > 0) AND ($offset < $epsposend))) {
22047                                                                                 // shift EPS images
22048                                                                                 $trx = sprintf('1 0 0 1 %.3F 0 cm', $spacew);
22049                                                                                 $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, ($prev_epsposbeg - 6));
22050                                                                                 $pmid_b = substr($pmid, 0, $epsposbeg);
22051                                                                                 $pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg));
22052                                                                                 $pmid_e = substr($pmid, $epsposend);
22053                                                                                 $pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e;
22054                                                                                 $offset = $epsposend;
22055                                                                                 continue;
22056 
22057                                                                         }
22058                                                                         $prev_epsposbeg = $epsposbeg;
22059                                                                         $currentxpos = 0;
22060                                                                         // shift blocks of code
22061                                                                         switch ($strpiece[2][0]) {
22062                                                                                 case 'Td':
22063                                                                                 case 'cm':
22064                                                                                 case 'm':
22065                                                                                 case 'l': {
22066                                                                                         // get current X position
22067                                                                                         preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
22068                                                                                         $currentxpos = $xmatches[1];
22069                                                                                         $textpos = $currentxpos;
22070                                                                                         if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) {
22071                                                                                                 $ns = $lnstring[3][$strcount];
22072                                                                                                 if ($this->isRTLTextDir()) {
22073                                                                                                         $spacew = ($spacewidth * ($nsmax - $ns));
22074                                                                                                 }
22075                                                                                                 ++$strcount;
22076                                                                                         }
22077                                                                                         // justify block
22078                                                                                         $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x',
22079                                                                                                 create_function('$matches', 'global $spacew;
22080                                                                                                 $newx = sprintf("%.2F",(floatval($matches[1]) + $spacew));
22081                                                                                                 return "".$newx." ".$matches[2]." x*#!#*x".$matches[3].$matches[4];'), $pmid, 1);
22082                                                                                         break;
22083                                                                                 }
22084                                                                                 case 're': {
22085                                                                                         // justify block
22086                                                                                         if (!$this->empty_string($this->lispacer)) {
22087                                                                                                 $this->lispacer = '';
22088                                                                                                 continue;
22089                                                                                         }
22090                                                                                         preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $xmatches);
22091                                                                                         $currentxpos = $xmatches[1];
22092                                                                                         global $x_diff, $w_diff;
22093                                                                                         $x_diff = 0;
22094                                                                                         $w_diff = 0;
22095                                                                                         if ($this->isRTLTextDir()) { // RTL
22096                                                                                                 if ($currentxpos < $textpos) {
22097                                                                                                         $x_diff = ($spacewidth * ($nsmax - $lnstring[3][$strcount]));
22098                                                                                                         $w_diff = ($spacewidth * $lnstring[2][$strcount]);
22099                                                                                                 } else {
22100                                                                                                         if ($strcount > 0) {
22101                                                                                                                 $x_diff = ($spacewidth * ($nsmax - $lnstring[3][($strcount - 1)]));
22102                                                                                                                 $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
22103                                                                                                         }
22104                                                                                                 }
22105                                                                                         } else { // LTR
22106                                                                                                 if ($currentxpos > $textpos) {
22107                                                                                                         if ($strcount > 0) {
22108                                                                                                                 $x_diff = ($spacewidth * $lnstring[3][($strcount - 1)]);
22109                                                                                                         }
22110                                                                                                         $w_diff = ($spacewidth * $lnstring[2][$strcount]);
22111                                                                                                 } else {
22112                                                                                                         if ($strcount > 1) {
22113                                                                                                                 $x_diff = ($spacewidth * $lnstring[3][($strcount - 2)]);
22114                                                                                                         }
22115                                                                                                         if ($strcount > 0) {
22116                                                                                                                 $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
22117                                                                                                         }
22118                                                                                                 }
22119                                                                                         }
22120                                                                                         $pmid = preg_replace_callback('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x',
22121                                                                                                 create_function('$matches', 'global $x_diff, $w_diff;
22122                                                                                                 $newx = sprintf("%.2F",(floatval($matches[1]) + $x_diff));
22123                                                                                                 $neww = sprintf("%.2F",(floatval($matches[3]) + $w_diff));
22124                                                                                                 return "".$newx." ".$matches[2]." ".$neww." ".$matches[4]." x*#!#*x".$matches[5].$matches[6];'), $pmid, 1);
22125                                                                                         break;
22126                                                                                 }
22127                                                                                 case 'c': {
22128                                                                                         // get current X position
22129                                                                                         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);
22130                                                                                         $currentxpos = $xmatches[1];
22131                                                                                         // justify block
22132                                                                                         $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',
22133                                                                                                 create_function('$matches', 'global $spacew;
22134                                                                                                 $newx1 = sprintf("%.3F",(floatval($matches[1]) + $spacew));
22135                                                                                                 $newx2 = sprintf("%.3F",(floatval($matches[3]) + $spacew));
22136                                                                                                 $newx3 = sprintf("%.3F",(floatval($matches[5]) + $spacew));
22137                                                                                                 return "".$newx1." ".$matches[2]." ".$newx2." ".$matches[4]." ".$newx3." ".$matches[6]." x*#!#*x".$matches[7].$matches[8];'), $pmid, 1);
22138                                                                                         break;
22139                                                                                 }
22140                                                                         }
22141                                                                         // shift the annotations and links
22142                                                                         $cxpos = ($currentxpos / $this->k);
22143                                                                         $lmpos = ($this->lMargin + $this->cell_padding['L'] + $this->feps);
22144                                                                         if ($this->inxobj) {
22145                                                                                 // we are inside an XObject template
22146                                                                                 foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
22147                                                                                         if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
22148                                                                                                 if ($cxpos > $lmpos) {
22149                                                                                                         $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += ($spacew / $this->k);
22150                                                                                                         $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
22151                                                                                                 } else {
22152                                                                                                         $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
22153                                                                                                 }
22154                                                                                                 break;
22155                                                                                         }
22156                                                                                 }
22157                                                                         } elseif (isset($this->PageAnnots[$this->page])) {
22158                                                                                 foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
22159                                                                                         if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
22160                                                                                                 if ($cxpos > $lmpos) {
22161                                                                                                         $this->PageAnnots[$this->page][$pak]['x'] += ($spacew / $this->k);
22162                                                                                                         $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
22163                                                                                                 } else {
22164                                                                                                         $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
22165                                                                                                 }
22166                                                                                                 break;
22167                                                                                         }
22168                                                                                 }
22169                                                                         }
22170                                                                 } // end of while
22171                                                                 // remove markers
22172                                                                 $pmid = str_replace('x*#!#*x', '', $pmid);
22173                                                                 if ($this->isUnicodeFont()) {
22174                                                                         // multibyte characters
22175                                                                         $spacew = $spacewidthu;
22176                                                                         if ($this->font_stretching != 100) {
22177                                                                                 // word spacing is affected by stretching
22178                                                                                 $spacew /= ($this->font_stretching / 100);
22179                                                                         }
22180                                                                         $pmidtemp = $pmid;
22181                                                                         // escape special characters
22182                                                                         $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
22183                                                                         $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
22184                                                                         $pmid = preg_replace_callback("/\[\(([^\)]*)\)\]/x",
22185                                                                                                 create_function('$matches', 'global $spacew;
22186                                                                                                 $matches[1] = str_replace("#!#OP#!#", "(", $matches[1]);
22187                                                                                                 $matches[1] = str_replace("#!#CP#!#", ")", $matches[1]);
22188                                                                                                 return "[(".str_replace(chr(0).chr(32), ") ".sprintf("%.3F", $spacew)." (", $matches[1]).")]";'), $pmidtemp);
22189                                                                         if ($this->inxobj) {
22190                                                                                 // we are inside an XObject template
22191                                                                                 $this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\n".$pend;
22192                                                                         } else {
22193                                                                                 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend);
22194                                                                         }
22195                                                                         $endlinepos = strlen($pstart."\n".$pmid."\n");
22196                                                                 } else {
22197                                                                         // non-unicode (single-byte characters)
22198                                                                         if ($this->font_stretching != 100) {
22199                                                                                 // word spacing (Tw) is affected by stretching
22200                                                                                 $spacewidth /= ($this->font_stretching / 100);
22201                                                                         }
22202                                                                         $rs = sprintf('%.3F Tw', $spacewidth);
22203                                                                         $pmid = preg_replace("/\[\(/x", $rs.' [(', $pmid);
22204                                                                         if ($this->inxobj) {
22205                                                                                 // we are inside an XObject template
22206                                                                                 $this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend;
22207                                                                         } else {
22208                                                                                 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend);
22209                                                                         }
22210                                                                         $endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n");
22211                                                                 }
22212                                                         }
22213                                                 } // end of J
22214                                         } // end if $startlinex
22215                                         if (($t_x != 0) OR ($yshift < 0)) {
22216                                                 // shift the line
22217                                                 $trx = sprintf('1 0 0 1 %.3F %.3F cm', ($t_x * $this->k), ($yshift * $this->k));
22218                                                 $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
22219                                                 $endlinepos = strlen($pstart);
22220                                                 if ($this->inxobj) {
22221                                                         // we are inside an XObject template
22222                                                         $this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend;
22223                                                         foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
22224                                                                 if ($pak >= $pask) {
22225                                                                         $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x;
22226                                                                         $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift;
22227                                                                 }
22228                                                         }
22229                                                 } else {
22230                                                         $this->setPageBuffer($startlinepage, $pstart.$pend);
22231                                                         // shift the annotations and links
22232                                                         if (isset($this->PageAnnots[$this->page])) {
22233                                                                 foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
22234                                                                         if ($pak >= $pask) {
22235                                                                                 $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
22236                                                                                 $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
22237                                                                         }
22238                                                                 }
22239                                                         }
22240                                                 }
22241                                                 $this->y -= $yshift;
22242                                         }
22243                                 }
22244                                 $pbrk = $this->checkPageBreak($this->lasth);
22245                                 $this->newline = false;
22246                                 $startlinex = $this->x;
22247                                 $startliney = $this->y;
22248                                 if ($dom[$dom[$key]['parent']]['value'] == 'sup') {
22249                                         $startliney -= ((0.3 * $this->FontSizePt) / $this->k);
22250                                 } elseif ($dom[$dom[$key]['parent']]['value'] == 'sub') {
22251                                         $startliney -= (($this->FontSizePt / 0.7) / $this->k);
22252                                 } else {
22253                                         $minstartliney = $startliney;
22254                                         $maxbottomliney = ($this->y + (($fontsize * $this->cell_height_ratio) / $this->k));
22255                                 }
22256                                 $startlinepage = $this->page;
22257                                 if (isset($endlinepos) AND (!$pbrk)) {
22258                                         $startlinepos = $endlinepos;
22259                                 } else {
22260                                         if ($this->inxobj) {
22261                                                 // we are inside an XObject template
22262                                                 $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']);
22263                                         } elseif (!$this->InFooter) {
22264                                                 if (isset($this->footerlen[$this->page])) {
22265                                                         $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
22266                                                 } else {
22267                                                         $this->footerpos[$this->page] = $this->pagelen[$this->page];
22268                                                 }
22269                                                 $startlinepos = $this->footerpos[$this->page];
22270                                         } else {
22271                                                 $startlinepos = $this->pagelen[$this->page];
22272                                         }
22273                                 }
22274                                 unset($endlinepos);
22275                                 $plalign = $lalign;
22276                                 if (isset($this->PageAnnots[$this->page])) {
22277                                         $pask = count($this->PageAnnots[$this->page]);
22278                                 } else {
22279                                         $pask = 0;
22280                                 }
22281                                 if (!($dom[$key]['tag'] AND !$dom[$key]['opening'] AND ($dom[$key]['value'] == 'table')
22282                                         AND (isset($this->emptypagemrk[$this->page]))
22283                                         AND ($this->emptypagemrk[$this->page] == $this->pagelen[$this->page]))) {
22284                                         $this->SetFont($fontname, $fontstyle, $fontsize);
22285                                         if ($wfill) {
22286                                                 $this->SetFillColorArray($this->bgcolor);
22287                                         }
22288                                 }
22289                         } // end newline
22290                         if (isset($opentagpos)) {
22291                                 unset($opentagpos);
22292                         }
22293                         if ($dom[$key]['tag']) {
22294                                 if ($dom[$key]['opening']) {
22295                                         // get text indentation (if any)
22296                                         if (isset($dom[$key]['text-indent']) AND $dom[$key]['block']) {
22297                                                 $this->textindent = $dom[$key]['text-indent'];
22298                                                 $this->newline = true;
22299                                         }
22300                                         // table
22301                                         if ($dom[$key]['value'] == 'table') {
22302                                                 // available page width
22303                                                 if ($this->rtl) {
22304                                                         $wtmp = $this->x - $this->lMargin;
22305                                                 } else {
22306                                                         $wtmp = $this->w - $this->rMargin - $this->x;
22307                                                 }
22308                                                 // get cell spacing
22309                                                 if (isset($dom[$key]['attribute']['cellspacing'])) {
22310                                                         $clsp = $this->getHTMLUnitToUnits($dom[$key]['attribute']['cellspacing'], 1, 'px');
22311                                                         $cellspacing = array('H' => $clsp, 'V' => $clsp);
22312                                                 } elseif (isset($dom[$key]['border-spacing'])) {
22313                                                         $cellspacing = $dom[$key]['border-spacing'];
22314                                                 } else {
22315                                                         $cellspacing = array('H' => 0, 'V' => 0);
22316                                                 }
22317                                                 // table width
22318                                                 if (isset($dom[$key]['width'])) {
22319                                                         $table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px');
22320                                                 } else {
22321                                                         $table_width = $wtmp;
22322                                                 }
22323                                                 $table_width -= (2 * $cellspacing['H']);
22324                                                 if (!$this->inthead) {
22325                                                         $this->y += $cellspacing['V'];
22326                                                 }
22327                                                 if ($this->rtl) {
22328                                                         $cellspacingx = -$cellspacing['H'];
22329                                                 } else {
22330                                                         $cellspacingx = $cellspacing['H'];
22331                                                 }
22332                                                 // total table width without cellspaces
22333                                                 $table_columns_width = ($table_width - ($cellspacing['H'] * ($dom[$key]['cols'] - 1)));
22334                                                 // minimum column width
22335                                                 $table_min_column_width = ($table_columns_width / $dom[$key]['cols']);
22336                                                 // array of custom column widths
22337                                                 $table_colwidths = array_fill(0, $dom[$key]['cols'], $table_min_column_width);
22338                                         }
22339                                         // table row
22340                                         if ($dom[$key]['value'] == 'tr') {
22341                                                 // reset column counter
22342                                                 $colid = 0;
22343                                         }
22344                                         // table cell
22345                                         if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
22346                                                 $trid = $dom[$key]['parent'];
22347                                                 $table_el = $dom[$trid]['parent'];
22348                                                 if (!isset($dom[$table_el]['cols'])) {
22349                                                         $dom[$table_el]['cols'] = $dom[$trid]['cols'];
22350                                                 }
22351                                                 // store border info
22352                                                 $tdborder = 0;
22353                                                 if (isset($dom[$key]['border']) AND !empty($dom[$key]['border'])) {
22354                                                         $tdborder = $dom[$key]['border'];
22355                                                 }
22356                                                 $colspan = $dom[$key]['attribute']['colspan'];
22357                                                 $old_cell_padding = $this->cell_padding;
22358                                                 if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) {
22359                                                         $crclpd = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px');
22360                                                         $current_cell_padding = array('L' => $crclpd, 'T' => $crclpd, 'R' => $crclpd, 'B' => $crclpd);
22361                                                 } elseif (isset($dom[($dom[$trid]['parent'])]['padding'])) {
22362                                                         $current_cell_padding = $dom[($dom[$trid]['parent'])]['padding'];
22363                                                 } else {
22364                                                         $current_cell_padding = array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0);
22365                                                 }
22366                                                 $this->cell_padding = $current_cell_padding;
22367                                                 if (isset($dom[$key]['height'])) {
22368                                                         // minimum cell height
22369                                                         $cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px');
22370                                                 } else {
22371                                                         $cellh = 0;
22372                                                 }
22373                                                 if (isset($dom[$key]['content'])) {
22374                                                         $cell_content = stripslashes($dom[$key]['content']);
22375                                                 } else {
22376                                                         $cell_content = '&nbsp;';
22377                                                 }
22378                                                 $tagtype = $dom[$key]['value'];
22379                                                 $parentid = $key;
22380                                                 while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) {
22381                                                         // move $key index forward
22382                                                         ++$key;
22383                                                 }
22384                                                 if (!isset($dom[$trid]['startpage'])) {
22385                                                         $dom[$trid]['startpage'] = $this->page;
22386                                                 } else {
22387                                                         $this->setPage($dom[$trid]['startpage']);
22388                                                 }
22389                                                 if (!isset($dom[$trid]['startcolumn'])) {
22390                                                         $dom[$trid]['startcolumn'] = $this->current_column;
22391                                                 } elseif ($this->current_column != $dom[$trid]['startcolumn']) {
22392                                                         $tmpx = $this->x;
22393                                                         $this->selectColumn($dom[$trid]['startcolumn']);
22394                                                         $this->x = $tmpx;
22395                                                 }
22396                                                 if (!isset($dom[$trid]['starty'])) {
22397                                                         $dom[$trid]['starty'] = $this->y;
22398                                                 } else {
22399                                                         $this->y = $dom[$trid]['starty'];
22400                                                 }
22401                                                 if (!isset($dom[$trid]['startx'])) {
22402                                                         $dom[$trid]['startx'] = $this->x;
22403                                                         $this->x += $cellspacingx;
22404                                                 } else {
22405                                                         $this->x += ($cellspacingx / 2);
22406                                                 }
22407                                                 if (isset($dom[$parentid]['attribute']['rowspan'])) {
22408                                                         $rowspan = intval($dom[$parentid]['attribute']['rowspan']);
22409                                                 } else {
22410                                                         $rowspan = 1;
22411                                                 }
22412                                                 // skip row-spanned cells started on the previous rows
22413                                                 if (isset($dom[$table_el]['rowspans'])) {
22414                                                         $rsk = 0;
22415                                                         $rskmax = count($dom[$table_el]['rowspans']);
22416                                                         while ($rsk < $rskmax) {
22417                                                                 $trwsp = $dom[$table_el]['rowspans'][$rsk];
22418                                                                 $rsstartx = $trwsp['startx'];
22419                                                                 $rsendx = $trwsp['endx'];
22420                                                                 // account for margin changes
22421                                                                 if ($trwsp['startpage'] < $this->page) {
22422                                                                         if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$trwsp['startpage']]['orm'])) {
22423                                                                                 $dl = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$trwsp['startpage']]['orm']);
22424                                                                                 $rsstartx -= $dl;
22425                                                                                 $rsendx -= $dl;
22426                                                                         } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$trwsp['startpage']]['olm'])) {
22427                                                                                 $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$trwsp['startpage']]['olm']);
22428                                                                                 $rsstartx += $dl;
22429                                                                                 $rsendx += $dl;
22430                                                                         }
22431                                                                 }
22432                                                                 if (($trwsp['rowspan'] > 0)
22433                                                                         AND ($rsstartx > ($this->x - $cellspacing['H'] - $current_cell_padding['L'] - $this->feps))
22434                                                                         AND ($rsstartx < ($this->x + $cellspacing['H'] + $current_cell_padding['R'] + $this->feps))
22435                                                                         AND (($trwsp['starty'] < ($this->y - $this->feps)) OR ($trwsp['startpage'] < $this->page) OR ($trwsp['startcolumn'] < $this->current_column))) {
22436                                                                         // set the starting X position of the current cell
22437                                                                         $this->x = $rsendx + $cellspacingx;
22438                                                                         // increment column indicator
22439                                                                         $colid += $trwsp['colspan'];
22440                                                                         if (($trwsp['rowspan'] == 1)
22441                                                                                 AND (isset($dom[$trid]['endy']))
22442                                                                                 AND (isset($dom[$trid]['endpage']))
22443                                                                                 AND (isset($dom[$trid]['endcolumn']))
22444                                                                                 AND ($trwsp['endpage'] == $dom[$trid]['endpage'])
22445                                                                                 AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
22446                                                                                 // set ending Y position for row
22447                                                                                 $dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
22448                                                                                 $dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy'];
22449                                                                         }
22450                                                                         $rsk = 0;
22451                                                                 } else {
22452                                                                         ++$rsk;
22453                                                                 }
22454                                                         }
22455                                                 }
22456                                                 if (isset($dom[$parentid]['width'])) {
22457                                                         // user specified width
22458                                                         $cellw = $this->getHTMLUnitToUnits($dom[$parentid]['width'], $table_columns_width, 'px');
22459                                                         $tmpcw = ($cellw / $colspan);
22460                                                         for ($i = 0; $i < $colspan; ++$i) {
22461                                                                 $table_colwidths[($colid + $i)] = $tmpcw;
22462                                                         }
22463                                                 } else {
22464                                                         // inherit column width
22465                                                         $cellw = 0;
22466                                                         for ($i = 0; $i < $colspan; ++$i) {
22467                                                                 $cellw += $table_colwidths[($colid + $i)];
22468                                                         }
22469                                                 }
22470                                                 $cellw += (($colspan - 1) * $cellspacing['H']);
22471                                                 // increment column indicator
22472                                                 $colid += $colspan;
22473                                                 // add rowspan information to table element
22474                                                 if ($rowspan > 1) {
22475                                                         $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));
22476                                                 }
22477                                                 $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x));
22478                                                 if ($rowspan > 1) {
22479                                                         $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1);
22480                                                 }
22481                                                 // push background colors
22482                                                 if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) {
22483                                                         $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor'];
22484                                                 }
22485                                                 // store border info
22486                                                 if (isset($tdborder) AND !empty($tdborder)) {
22487                                                         $dom[$trid]['cellpos'][($cellid - 1)]['border'] = $tdborder;
22488                                                 }
22489                                                 $prevLastH = $this->lasth;
22490                                                 // store some info for multicolumn mode
22491                                                 if ($this->rtl) {
22492                                                         $this->colxshift['x'] = $this->w - $this->x - $this->rMargin;
22493                                                 } else {
22494                                                         $this->colxshift['x'] = $this->x - $this->lMargin;
22495                                                 }
22496                                                 $this->colxshift['s'] = $cellspacing;
22497                                                 $this->colxshift['p'] = $current_cell_padding;
22498                                                 // ****** write the cell content ******
22499                                                 $this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true, true, 0, 'T', false);
22500                                                 // restore some values
22501                                                 $this->colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
22502                                                 $this->lasth = $prevLastH;
22503                                                 $this->cell_padding = $old_cell_padding;
22504                                                 $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x;
22505                                                 // update the end of row position
22506                                                 if ($rowspan <= 1) {
22507                                                         if (isset($dom[$trid]['endy'])) {
22508                                                                 if (($this->page == $dom[$trid]['endpage']) AND ($this->current_column == $dom[$trid]['endcolumn'])) {
22509                                                                         $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']);
22510                                                                 } elseif (($this->page > $dom[$trid]['endpage']) OR ($this->current_column > $dom[$trid]['endcolumn'])) {
22511                                                                         $dom[$trid]['endy'] = $this->y;
22512                                                                 }
22513                                                         } else {
22514                                                                 $dom[$trid]['endy'] = $this->y;
22515                                                         }
22516                                                         if (isset($dom[$trid]['endpage'])) {
22517                                                                 $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']);
22518                                                         } else {
22519                                                                 $dom[$trid]['endpage'] = $this->page;
22520                                                         }
22521                                                         if (isset($dom[$trid]['endcolumn'])) {
22522                                                                 $dom[$trid]['endcolumn'] = max($this->current_column, $dom[$trid]['endcolumn']);
22523                                                         } else {
22524                                                                 $dom[$trid]['endcolumn'] = $this->current_column;
22525                                                         }
22526                                                 } else {
22527                                                         // account for row-spanned cells
22528                                                         $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x;
22529                                                         $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y;
22530                                                         $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page;
22531                                                         $dom[$table_el]['rowspans'][($trsid - 1)]['endcolumn'] = $this->current_column;
22532                                                 }
22533                                                 if (isset($dom[$table_el]['rowspans'])) {
22534                                                         // update endy and endpage on rowspanned cells
22535                                                         foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
22536                                                                 if ($trwsp['rowspan'] > 0) {
22537                                                                         if (isset($dom[$trid]['endpage'])) {
22538                                                                                 if (($trwsp['endpage'] == $dom[$trid]['endpage']) AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
22539                                                                                         $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
22540                                                                                 } elseif (($trwsp['endpage'] < $dom[$trid]['endpage']) OR ($trwsp['endcolumn'] < $dom[$trid]['endcolumn'])) {
22541                                                                                         $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy'];
22542                                                                                         $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage'];
22543                                                                                         $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[$trid]['endcolumn'];
22544                                                                                 } else {
22545                                                                                         $dom[$trid]['endy'] = $this->pagedim[$dom[$trid]['endpage']]['hk'] - $this->pagedim[$dom[$trid]['endpage']]['bm'];
22546                                                                                 }
22547                                                                         }
22548                                                                 }
22549                                                         }
22550                                                 }
22551                                                 $this->x += ($cellspacingx / 2);
22552                                         } else {
22553                                                 // opening tag (or self-closing tag)
22554                                                 if (!isset($opentagpos)) {
22555                                                         if ($this->inxobj) {
22556                                                                 // we are inside an XObject template
22557                                                                 $opentagpos = strlen($this->xobjects[$this->xobjid]['outdata']);
22558                                                         } elseif (!$this->InFooter) {
22559                                                                 if (isset($this->footerlen[$this->page])) {
22560                                                                         $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
22561                                                                 } else {
22562                                                                         $this->footerpos[$this->page] = $this->pagelen[$this->page];
22563                                                                 }
22564                                                                 $opentagpos = $this->footerpos[$this->page];
22565                                                         }
22566                                                 }
22567                                                 $dom = $this->openHTMLTagHandler($dom, $key, $cell);
22568                                         }
22569                                 } else { // closing tag
22570                                         $prev_numpages = $this->numpages;
22571                                         $old_bordermrk = $this->bordermrk[$this->page];
22572                                         $dom = $this->closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney);
22573                                         if ($this->bordermrk[$this->page] > $old_bordermrk) {
22574                                                 $startlinepos += ($this->bordermrk[$this->page] - $old_bordermrk);
22575                                         }
22576                                         if ($prev_numpages > $this->numpages) {
22577                                                 $startlinepage = $this->page;
22578                                         }
22579                                 }
22580                         } elseif (strlen($dom[$key]['value']) > 0) {
22581                                 // print list-item
22582                                 if (!$this->empty_string($this->lispacer) AND ($this->lispacer != '^')) {
22583                                         $this->SetFont($pfontname, $pfontstyle, $pfontsize);
22584                                         $this->resetLastH();
22585                                         $minstartliney = $this->y;
22586                                         $maxbottomliney = ($startliney + ($this->FontSize * $this->cell_height_ratio));
22587                                         $this->putHtmlListBullet($this->listnum, $this->lispacer, $pfontsize);
22588                                         $this->SetFont($curfontname, $curfontstyle, $curfontsize);
22589                                         $this->resetLastH();
22590                                         if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) {
22591                                                 $pfontascent = $this->getFontAscent($pfontname, $pfontstyle, $pfontsize);
22592                                                 $pfontdescent = $this->getFontDescent($pfontname, $pfontstyle, $pfontsize);
22593                                                 $this->y += ((($pfontsize - $curfontsize) * $this->cell_height_ratio / $this->k) + $pfontascent - $curfontascent - $pfontdescent + $curfontdescent) / 2;
22594                                                 $minstartliney = min($this->y, $minstartliney);
22595                                                 $maxbottomliney = max(($this->y + (($pfontsize * $this->cell_height_ratio) / $this->k)), $maxbottomliney);
22596                                         }
22597                                 }
22598                                 // text
22599                                 $this->htmlvspace = 0;
22600                                 if ((!$this->premode) AND $this->isRTLTextDir()) {
22601                                         // reverse spaces order
22602                                         $lsp = ''; // left spaces
22603                                         $rsp = ''; // right spaces
22604                                         if (preg_match('/^('.$this->re_space['p'].'+)/'.$this->re_space['m'], $dom[$key]['value'], $matches)) {
22605                                                 $lsp = $matches[1];
22606                                         }
22607                                         if (preg_match('/('.$this->re_space['p'].'+)$/'.$this->re_space['m'], $dom[$key]['value'], $matches)) {
22608                                                 $rsp = $matches[1];
22609                                         }
22610                                         $dom[$key]['value'] = $rsp.$this->stringTrim($dom[$key]['value']).$lsp;
22611                                 }
22612                                 if ($newline) {
22613                                         if (!$this->premode) {
22614                                                 $prelen = strlen($dom[$key]['value']);
22615                                                 if ($this->isRTLTextDir()) {
22616                                                         // right trim except non-breaking space
22617                                                         $dom[$key]['value'] = $this->stringRightTrim($dom[$key]['value']);
22618                                                 } else {
22619                                                         // left trim except non-breaking space
22620                                                         $dom[$key]['value'] = $this->stringLeftTrim($dom[$key]['value']);
22621                                                 }
22622                                                 $postlen = strlen($dom[$key]['value']);
22623                                                 if (($postlen == 0) AND ($prelen > 0)) {
22624                                                         $dom[$key]['trimmed_space'] = true;
22625                                                 }
22626                                         }
22627                                         $newline = false;
22628                                         $firstblock = true;
22629                                 } else {
22630                                         $firstblock = false;
22631                                         // replace empty multiple spaces string with a single space
22632                                         $dom[$key]['value'] = preg_replace('/^'.$this->re_space['p'].'+$/'.$this->re_space['m'], chr(32), $dom[$key]['value']);
22633                                 }
22634                                 $strrest = '';
22635                                 if ($this->rtl) {
22636                                         $this->x -= $this->textindent;
22637                                 } else {
22638                                         $this->x += $this->textindent;
22639                                 }
22640                                 if (!isset($dom[$key]['trimmed_space']) OR !$dom[$key]['trimmed_space']) {
22641                                         $strlinelen = $this->GetStringWidth($dom[$key]['value']);
22642                                         if (!empty($this->HREF) AND (isset($this->HREF['url']))) {
22643                                                 // HTML <a> Link
22644                                                 $hrefcolor = '';
22645                                                 if (isset($dom[($dom[$key]['parent'])]['fgcolor']) AND ($dom[($dom[$key]['parent'])]['fgcolor'] !== false)) {
22646                                                         $hrefcolor = $dom[($dom[$key]['parent'])]['fgcolor'];
22647                                                 }
22648                                                 $hrefstyle = -1;
22649                                                 if (isset($dom[($dom[$key]['parent'])]['fontstyle']) AND ($dom[($dom[$key]['parent'])]['fontstyle'] !== false)) {
22650                                                         $hrefstyle = $dom[($dom[$key]['parent'])]['fontstyle'];
22651                                                 }
22652                                                 $strrest = $this->addHtmlLink($this->HREF['url'], $dom[$key]['value'], $wfill, true, $hrefcolor, $hrefstyle, true);
22653                                         } else {
22654                                                 $wadj = 0; // space to leave for block continuity
22655                                                 if ($this->rtl) {
22656                                                         $cwa = $this->x - $this->lMargin;
22657                                                 } else {
22658                                                         $cwa = $this->w - $this->rMargin - $this->x;
22659                                                 }
22660                                                 if (($strlinelen < $cwa) AND (isset($dom[($key + 1)])) AND ($dom[($key + 1)]['tag']) AND (!$dom[($key + 1)]['block'])) {
22661                                                         // check the next text blocks for continuity
22662                                                         $nkey = ($key + 1);
22663                                                         $write_block = true;
22664                                                         $same_textdir = true;
22665                                                         $tmp_fontname = $this->FontFamily;
22666                                                         $tmp_fontstyle = $this->FontStyle;
22667                                                         $tmp_fontsize = $this->FontSizePt;
22668                                                         while ($write_block AND isset($dom[$nkey])) {
22669                                                                 if ($dom[$nkey]['tag']) {
22670                                                                         if ($dom[$nkey]['block']) {
22671                                                                                 // end of block
22672                                                                                 $write_block = false;
22673                                                                         }
22674                                                                         $tmp_fontname = isset($dom[$nkey]['fontname']) ? $dom[$nkey]['fontname'] : $this->FontFamily;
22675                                                                         $tmp_fontstyle = isset($dom[$nkey]['fontstyle']) ? $dom[$nkey]['fontstyle'] : $this->FontStyle;
22676                                                                         $tmp_fontsize = isset($dom[$nkey]['fontsize']) ? $dom[$nkey]['fontsize'] : $this->FontSizePt;
22677                                                                         $same_textdir = ($dom[$nkey]['dir'] == $dom[$key]['dir']);
22678                                                                 } else {
22679                                                                         $nextstr = preg_split('/'.$this->re_space['p'].'+/'.$this->re_space['m'], $dom[$nkey]['value']);
22680                                                                         if (isset($nextstr[0]) AND $same_textdir) {
22681                                                                                 $wadj += $this->GetStringWidth($nextstr[0], $tmp_fontname, $tmp_fontstyle, $tmp_fontsize);
22682                                                                         }
22683                                                                         if (isset($nextstr[1])) {
22684                                                                                 $write_block = false;
22685                                                                         }
22686                                                                 }
22687                                                                 ++$nkey;
22688                                                         }
22689                                                 }
22690                                                 if (($wadj > 0) AND (($strlinelen + $wadj) >= $cwa)) {
22691                                                         $wadj = 0;
22692                                                         $nextstr = preg_split('/'.$this->re_space['p'].'/'.$this->re_space['m'], $dom[$key]['value']);
22693                                                         $numblks = count($nextstr);
22694                                                         if ($numblks > 1) {
22695                                                                 // try to split on blank spaces
22696                                                                 $wadj = ($cwa - $strlinelen + $this->GetStringWidth($nextstr[($numblks - 1)]));
22697                                                         }
22698                                                 }
22699                                                 // check for reversed text direction
22700                                                 if (($wadj > 0) AND (($this->rtl AND ($this->tmprtl === 'L')) OR (!$this->rtl AND ($this->tmprtl === 'R')))) {
22701                                                         // LTR text on RTL direction or RTL text on LTR direction
22702                                                         $reverse_dir = true;
22703                                                         $this->rtl = !$this->rtl;
22704                                                         $revshift = ($strlinelen + $wadj + 0.000001); // add little quantity for rounding problems
22705                                                         if ($this->rtl) {
22706                                                                 $this->x += $revshift;
22707                                                         } else {
22708                                                                 $this->x -= $revshift;
22709                                                         }
22710                                                         $xws = $this->x;
22711                                                 }
22712                                                 // ****** write only until the end of the line and get the rest ******
22713                                                 $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock, 0, $wadj);
22714                                                 // restore default direction
22715                                                 if ($reverse_dir AND ($wadj == 0)) {
22716                                                         $this->x = $xws;
22717                                                         $this->rtl = !$this->rtl;
22718                                                         $reverse_dir = false;
22719                                                 }
22720                                         }
22721                                 }
22722                                 $this->textindent = 0;
22723                                 if (strlen($strrest) > 0) {
22724                                         // store the remaining string on the previous $key position
22725                                         $this->newline = true;
22726                                         if ($strrest == $dom[$key]['value']) {
22727                                                 // used to avoid infinite loop
22728                                                 ++$loop;
22729                                         } else {
22730                                                 $loop = 0;
22731                                         }
22732                                         $dom[$key]['value'] = $strrest;
22733                                         if ($cell) {
22734                                                 if ($this->rtl) {
22735                                                         $this->x -= $this->cell_padding['R'];
22736                                                 } else {
22737                                                         $this->x += $this->cell_padding['L'];
22738                                                 }
22739                                         }
22740                                         if ($loop < 3) {
22741                                                 --$key;
22742                                         }
22743                                 } else {
22744                                         $loop = 0;
22745                                 }
22746                         }
22747                         ++$key;
22748                         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')) {
22749                                 // check if we are on a new page or on a new column
22750                                 if ((!$undo) AND (($this->y < $this->start_transaction_y) OR (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['endy'] < $this->start_transaction_y)))) {
22751                                         // we are on a new page or on a new column and the total object height is less than the available vertical space.
22752                                         // restore previous object
22753                                         $this->rollbackTransaction(true);
22754                                         // restore previous values
22755                                         foreach ($this_method_vars as $vkey => $vval) {
22756                                                 $$vkey = $vval;
22757                                         }
22758                                         // add a page (or trig AcceptPageBreak() for multicolumn mode)
22759                                         $pre_y = $this->y;
22760                                         if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) {
22761                                                 $startliney = $this->y;
22762                                         }
22763                                         $undo = true; // avoid infinite loop
22764                                 } else {
22765                                         $undo = false;
22766                                 }
22767                         }
22768                 } // end for each $key
22769                 // align the last line
22770                 if (isset($startlinex)) {
22771                         $yshift = ($minstartliney - $startliney);
22772                         if (($yshift > 0) OR ($this->page > $startlinepage)) {
22773                                 $yshift = 0;
22774                         }
22775                         $t_x = 0;
22776                         // the last line must be shifted to be aligned as requested
22777                         $linew = abs($this->endlinex - $startlinex);
22778                         if ($this->inxobj) {
22779                                 // we are inside an XObject template
22780                                 $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos);
22781                                 if (isset($opentagpos)) {
22782                                         $midpos = $opentagpos;
22783                                 } else {
22784                                         $midpos = 0;
22785                                 }
22786                                 if ($midpos > 0) {
22787                                         $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos));
22788                                         $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos);
22789                                 } else {
22790                                         $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos);
22791                                         $pend = '';
22792                                 }
22793                         } else {
22794                                 $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
22795                                 if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
22796                                         $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
22797                                         $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
22798                                 } elseif (isset($opentagpos)) {
22799                                         $midpos = $opentagpos;
22800                                 } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
22801                                         $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
22802                                         $midpos = $this->footerpos[$startlinepage];
22803                                 } else {
22804                                         $midpos = 0;
22805                                 }
22806                                 if ($midpos > 0) {
22807                                         $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
22808                                         $pend = substr($this->getPageBuffer($startlinepage), $midpos);
22809                                 } else {
22810                                         $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
22811                                         $pend = '';
22812                                 }
22813                         }
22814                         if ((isset($plalign) AND ((($plalign == 'C') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) {
22815                                 // calculate shifting amount
22816                                 $tw = $w;
22817                                 if ($this->lMargin != $prevlMargin) {
22818                                         $tw += ($prevlMargin - $this->lMargin);
22819                                 }
22820                                 if ($this->rMargin != $prevrMargin) {
22821                                         $tw += ($prevrMargin - $this->rMargin);
22822                                 }
22823                                 $one_space_width = $this->GetStringWidth(chr(32));
22824                                 $no = 0; // number of spaces on a line contained on a single block
22825                                 if ($this->isRTLTextDir()) { // RTL
22826                                         // remove left space if exist
22827                                         $pos1 = $this->revstrpos($pmid, '[(');
22828                                         if ($pos1 > 0) {
22829                                                 $pos1 = intval($pos1);
22830                                                 if ($this->isUnicodeFont()) {
22831                                                         $pos2 = intval($this->revstrpos($pmid, '[('.chr(0).chr(32)));
22832                                                         $spacelen = 2;
22833                                                 } else {
22834                                                         $pos2 = intval($this->revstrpos($pmid, '[('.chr(32)));
22835                                                         $spacelen = 1;
22836                                                 }
22837                                                 if ($pos1 == $pos2) {
22838                                                         $pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen));
22839                                                         if (substr($pmid, $pos1, 4) == '[()]') {
22840                                                                 $linew -= $one_space_width;
22841                                                         } elseif ($pos1 == strpos($pmid, '[(')) {
22842                                                                 $no = 1;
22843                                                         }
22844                                                 }
22845                                         }
22846                                 } else { // LTR
22847                                         // remove right space if exist
22848                                         $pos1 = $this->revstrpos($pmid, ')]');
22849                                         if ($pos1 > 0) {
22850                                                 $pos1 = intval($pos1);
22851                                                 if ($this->isUnicodeFont()) {
22852                                                         $pos2 = intval($this->revstrpos($pmid, chr(0).chr(32).')]')) + 2;
22853                                                         $spacelen = 2;
22854                                                 } else {
22855                                                         $pos2 = intval($this->revstrpos($pmid, chr(32).')]')) + 1;
22856                                                         $spacelen = 1;
22857                                                 }
22858                                                 if ($pos1 == $pos2) {
22859                                                         $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
22860                                                         $linew -= $one_space_width;
22861                                                 }
22862                                         }
22863                                 }
22864                                 $mdiff = ($tw - $linew);
22865                                 if ($plalign == 'C') {
22866                                         if ($this->rtl) {
22867                                                 $t_x = -($mdiff / 2);
22868                                         } else {
22869                                                 $t_x = ($mdiff / 2);
22870                                         }
22871                                 } elseif ($plalign == 'R') {
22872                                         // right alignment on LTR document
22873                                         $t_x = $mdiff;
22874                                 } elseif ($plalign == 'L') {
22875                                         // left alignment on RTL document
22876                                         $t_x = -$mdiff;
22877                                 }
22878                         } // end if startlinex
22879                         if (($t_x != 0) OR ($yshift < 0)) {
22880                                 // shift the line
22881                                 $trx = sprintf('1 0 0 1 %.3F %.3F cm', ($t_x * $this->k), ($yshift * $this->k));
22882                                 $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
22883                                 $endlinepos = strlen($pstart);
22884                                 if ($this->inxobj) {
22885                                         // we are inside an XObject template
22886                                         $this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend;
22887                                         foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
22888                                                 if ($pak >= $pask) {
22889                                                         $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x;
22890                                                         $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift;
22891                                                 }
22892                                         }
22893                                 } else {
22894                                         $this->setPageBuffer($startlinepage, $pstart.$pend);
22895                                         // shift the annotations and links
22896                                         if (isset($this->PageAnnots[$this->page])) {
22897                                                 foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
22898                                                         if ($pak >= $pask) {
22899                                                                 $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
22900                                                                 $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
22901                                                         }
22902                                                 }
22903                                         }
22904                                 }
22905                                 $this->y -= $yshift;
22906                                 $yshift = 0;
22907                         }
22908                 }
22909                 // restore previous values
22910                 $this->setGraphicVars($gvars);
22911                 if ($this->num_columns > 1) {
22912                         $this->selectColumn();
22913                 } elseif ($this->page > $prevPage) {
22914                         $this->lMargin = $this->pagedim[$this->page]['olm'];
22915                         $this->rMargin = $this->pagedim[$this->page]['orm'];
22916                 }
22917                 // restore previous list state
22918                 $this->cell_height_ratio = $prev_cell_height_ratio;
22919                 $this->listnum = $prev_listnum;
22920                 $this->listordered = $prev_listordered;
22921                 $this->listcount = $prev_listcount;
22922                 $this->lispacer = $prev_lispacer;
22923                 if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) {
22924                         $this->Ln($this->lasth);
22925                         if ($this->y < $maxbottomliney) {
22926                                 $this->y = $maxbottomliney;
22927                         }
22928                 }
22929                 unset($dom);
22930         }
22931 
22940         protected function openHTMLTagHandler($dom, $key, $cell) {
22941                 $tag = $dom[$key];
22942                 $parent = $dom[($dom[$key]['parent'])];
22943                 $firsttag = ($key == 1);
22944                 // check for text direction attribute
22945                 if (isset($tag['dir'])) {
22946                         $this->setTempRTL($tag['dir']);
22947                 } else {
22948                         $this->tmprtl = false;
22949                 }
22950                 if ($tag['block']) {
22951                         $hbz = 0; // distance from y to line bottom
22952                         $hb = 0; // vertical space between block tags
22953                         // calculate vertical space for block tags
22954                         if (isset($this->tagvspaces[$tag['value']][0]['h']) AND ($this->tagvspaces[$tag['value']][0]['h'] >= 0)) {
22955                                 $cur_h = $this->tagvspaces[$tag['value']][0]['h'];
22956                         } elseif (isset($tag['fontsize'])) {
22957                                 $cur_h = ($tag['fontsize'] / $this->k) * $this->cell_height_ratio;
22958                         } else {
22959                                 $cur_h = $this->FontSize * $this->cell_height_ratio;
22960                         }
22961                         if (isset($this->tagvspaces[$tag['value']][0]['n'])) {
22962                                 $n = $this->tagvspaces[$tag['value']][0]['n'];
22963                         } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
22964                                 $n = 0.6;
22965                         } else {
22966                                 $n = 1;
22967                         }
22968                         if ((!isset($this->tagvspaces[$tag['value']])) AND (in_array($tag['value'], array('div', 'dt', 'dd', 'li', 'br')))) {
22969                                 $hb = 0;
22970                         } else {
22971                                 $hb = ($n * $cur_h);
22972                         }
22973                         if (($this->htmlvspace <= 0) AND ($n > 0)) {
22974                                 if (isset($parent['fontsize'])) {
22975                                         $hbz = (($parent['fontsize'] / $this->k) * $this->cell_height_ratio);
22976                                 } else {
22977                                         $hbz = $this->FontSize * $this->cell_height_ratio;
22978                                 }
22979                         }
22980                 }
22981                 // Opening tag
22982                 switch($tag['value']) {
22983                         case 'table': {
22984                                 $cp = 0;
22985                                 $cs = 0;
22986                                 $dom[$key]['rowspans'] = array();
22987                                 if (!isset($dom[$key]['attribute']['nested']) OR ($dom[$key]['attribute']['nested'] != 'true')) {
22988                                         // set table header
22989                                         if (!$this->empty_string($dom[$key]['thead'])) {
22990                                                 // set table header
22991                                                 $this->thead = $dom[$key]['thead'];
22992                                                 if (!isset($this->theadMargins) OR (empty($this->theadMargins))) {
22993                                                         $this->theadMargins = array();
22994                                                         $this->theadMargins['cell_padding'] = $this->cell_padding;
22995                                                         $this->theadMargins['lmargin'] = $this->lMargin;
22996                                                         $this->theadMargins['rmargin'] = $this->rMargin;
22997                                                         $this->theadMargins['page'] = $this->page;
22998                                                         $this->theadMargins['cell'] = $cell;
22999                                                 }
23000                                         }
23001                                 }
23002                                 // store current margins and page
23003                                 $dom[$key]['old_cell_padding'] = $this->cell_padding;
23004                                 if (isset($tag['attribute']['cellpadding'])) {
23005                                         $pad = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px');
23006                                         $this->SetCellPadding($pad);
23007                                 } elseif (isset($tag['padding'])) {
23008                                         $this->cell_padding = $tag['padding'];
23009                                 }
23010                                 if (isset($tag['attribute']['cellspacing'])) {
23011                                         $cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
23012                                 } elseif (isset($tag['border-spacing'])) {
23013                                         $cs = $tag['border-spacing']['V'];
23014                                 }
23015                                 $prev_y = $this->y;
23016                                 if ($this->checkPageBreak(((2 * $cp) + (2 * $cs) + $this->lasth), '', false) OR ($this->y < $prev_y)) {
23017                                         $this->inthead = true;
23018                                         // add a page (or trig AcceptPageBreak() for multicolumn mode)
23019                                         $this->checkPageBreak($this->PageBreakTrigger + 1);
23020                                 }
23021                                 break;
23022                         }
23023                         case 'tr': {
23024                                 // array of columns positions
23025                                 $dom[$key]['cellpos'] = array();
23026                                 break;
23027                         }
23028                         case 'hr': {
23029                                 if ((isset($tag['height'])) AND ($tag['height'] != '')) {
23030                                         $hrHeight = $this->getHTMLUnitToUnits($tag['height'], 1, 'px');
23031                                 } else {
23032                                         $hrHeight = $this->GetLineWidth();
23033                                 }
23034                                 $this->addHTMLVertSpace($hbz, ($hrHeight / 2), $cell, $firsttag);
23035                                 $x = $this->GetX();
23036                                 $y = $this->GetY();
23037                                 $wtmp = $this->w - $this->lMargin - $this->rMargin;
23038                                 if ($cell) {
23039                                         $wtmp -= ($this->cell_padding['L'] + $this->cell_padding['R']);
23040                                 }
23041                                 if ((isset($tag['width'])) AND ($tag['width'] != '')) {
23042                                         $hrWidth = $this->getHTMLUnitToUnits($tag['width'], $wtmp, 'px');
23043                                 } else {
23044                                         $hrWidth = $wtmp;
23045                                 }
23046                                 $prevlinewidth = $this->GetLineWidth();
23047                                 $this->SetLineWidth($hrHeight);
23048                                 $this->Line($x, $y, $x + $hrWidth, $y);
23049                                 $this->SetLineWidth($prevlinewidth);
23050                                 $this->addHTMLVertSpace(($hrHeight / 2), 0, $cell, !isset($dom[($key + 1)]));
23051                                 break;
23052                         }
23053                         case 'a': {
23054                                 if (array_key_exists('href', $tag['attribute'])) {
23055                                         $this->HREF['url'] = $tag['attribute']['href'];
23056                                 }
23057                                 break;
23058                         }
23059                         case 'img': {
23060                                 if (isset($tag['attribute']['src'])) {
23061                                         if ($tag['attribute']['src']{0} === '@') {
23062                                                 // data stream
23063                                                 $tag['attribute']['src'] = '@'.base64_decode(substr($tag['attribute']['src'], 1));
23064                                                 $type = '';
23065                                         } else {
23066                                                 // check for images without protocol
23067                                                 if (preg_match('%^/{2}%', $tag['attribute']['src'])) {
23068                                                         $tag['attribute']['src'] = 'http:'.$tag['attribute']['src'];
23069                                                 }
23070                                                 // replace relative path with real server path
23071                                                 if (($tag['attribute']['src'][0] == '/') AND !empty($_SERVER['DOCUMENT_ROOT']) AND ($_SERVER['DOCUMENT_ROOT'] != '/')) {
23072                                                         $findroot = strpos($tag['attribute']['src'], $_SERVER['DOCUMENT_ROOT']);
23073                                                         if (($findroot === false) OR ($findroot > 1)) {
23074                                                                 if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') {
23075                                                                         $tag['attribute']['src'] = substr($_SERVER['DOCUMENT_ROOT'], 0, -1).$tag['attribute']['src'];
23076                                                                 } else {
23077                                                                         $tag['attribute']['src'] = $_SERVER['DOCUMENT_ROOT'].$tag['attribute']['src'];
23078                                                                 }
23079                                                         }
23080                                                 }
23081                                                 $tag['attribute']['src'] = htmlspecialchars_decode(urldecode($tag['attribute']['src']));
23082                                                 $type = $this->getImageFileType($tag['attribute']['src']);
23083                                                 $testscrtype = @parse_url($tag['attribute']['src']);
23084                                                 if (!isset($testscrtype['query']) OR empty($testscrtype['query'])) {
23085                                                         // convert URL to server path
23086                                                         $tag['attribute']['src'] = str_replace(K_PATH_URL, K_PATH_MAIN, $tag['attribute']['src']);
23087                                                 }
23088                                         }
23089                                         if (!isset($tag['width'])) {
23090                                                 $tag['width'] = 0;
23091                                         }
23092                                         if (!isset($tag['height'])) {
23093                                                 $tag['height'] = 0;
23094                                         }
23095                                         //if (!isset($tag['attribute']['align'])) {
23096                                                 // the only alignment supported is "bottom"
23097                                                 // further development is required for other modes.
23098                                                 $tag['attribute']['align'] = 'bottom';
23099                                         //}
23100                                         switch($tag['attribute']['align']) {
23101                                                 case 'top': {
23102                                                         $align = 'T';
23103                                                         break;
23104                                                 }
23105                                                 case 'middle': {
23106                                                         $align = 'M';
23107                                                         break;
23108                                                 }
23109                                                 case 'bottom': {
23110                                                         $align = 'B';
23111                                                         break;
23112                                                 }
23113                                                 default: {
23114                                                         $align = 'B';
23115                                                         break;
23116                                                 }
23117                                         }
23118                                         $prevy = $this->y;
23119                                         $xpos = $this->x;
23120                                         $imglink = '';
23121                                         if (isset($this->HREF['url']) AND !$this->empty_string($this->HREF['url'])) {
23122                                                 $imglink = $this->HREF['url'];
23123                                                 if ($imglink{0} == '#') {
23124                                                         // convert url to internal link
23125                                                         $lnkdata = explode(',', $imglink);
23126                                                         if (isset($lnkdata[0])) {
23127                                                                 $page = intval(substr($lnkdata[0], 1));
23128                                                                 if (empty($page) OR ($page <= 0)) {
23129                                                                         $page = $this->page;
23130                                                                 }
23131                                                                 if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
23132                                                                         $lnky = floatval($lnkdata[1]);
23133                                                                 } else {
23134                                                                         $lnky = 0;
23135                                                                 }
23136                                                                 $imglink = $this->AddLink();
23137                                                                 $this->SetLink($imglink, $lnky, $page);
23138                                                         }
23139                                                 }
23140                                         }
23141                                         $border = 0;
23142                                         if (isset($tag['border']) AND !empty($tag['border'])) {
23143                                                 // currently only support 1 (frame) or a combination of 'LTRB'
23144                                                 $border = $tag['border'];
23145                                         }
23146                                         $iw = '';
23147                                         if (isset($tag['width'])) {
23148                                                 $iw = $this->getHTMLUnitToUnits($tag['width'], 1, 'px', false);
23149                                         }
23150                                         $ih = '';
23151                                         if (isset($tag['height'])) {
23152                                                 $ih = $this->getHTMLUnitToUnits($tag['height'], 1, 'px', false);
23153                                         }
23154                                         if (($type == 'eps') OR ($type == 'ai')) {
23155                                                 $this->ImageEps($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, true, $align, '', $border, true);
23156                                         } elseif ($type == 'svg') {
23157                                                 $this->ImageSVG($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, $align, '', $border, true);
23158                                         } else {
23159                                                 $this->Image($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border, false, false, true);
23160                                         }
23161                                         switch($align) {
23162                                                 case 'T': {
23163                                                         $this->y = $prevy;
23164                                                         break;
23165                                                 }
23166                                                 case 'M': {
23167                                                         $this->y = (($this->img_rb_y + $prevy - ($tag['fontsize'] / $this->k)) / 2) ;
23168                                                         break;
23169                                                 }
23170                                                 case 'B': {
23171                                                         $this->y = $this->img_rb_y - ($tag['fontsize'] / $this->k);
23172                                                         break;
23173                                                 }
23174                                         }
23175                                 }
23176                                 break;
23177                         }
23178                         case 'dl': {
23179                                 ++$this->listnum;
23180                                 if ($this->listnum == 1) {
23181                                         $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
23182                                 } else {
23183                                         $this->addHTMLVertSpace(0, 0, $cell, $firsttag);
23184                                 }
23185                                 break;
23186                         }
23187                         case 'dt': {
23188                                 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
23189                                 break;
23190                         }
23191                         case 'dd': {
23192                                 if ($this->rtl) {
23193                                         $this->rMargin += $this->listindent;
23194                                 } else {
23195                                         $this->lMargin += $this->listindent;
23196                                 }
23197                                 ++$this->listindentlevel;
23198                                 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
23199                                 break;
23200                         }
23201                         case 'ul':
23202                         case 'ol': {
23203                                 ++$this->listnum;
23204                                 if ($tag['value'] == 'ol') {
23205                                         $this->listordered[$this->listnum] = true;
23206                                 } else {
23207                                         $this->listordered[$this->listnum] = false;
23208                                 }
23209                                 if (isset($tag['attribute']['start'])) {
23210                                         $this->listcount[$this->listnum] = intval($tag['attribute']['start']) - 1;
23211                                 } else {
23212                                         $this->listcount[$this->listnum] = 0;
23213                                 }
23214                                 if ($this->rtl) {
23215                                         $this->rMargin += $this->listindent;
23216                                         $this->x -= $this->listindent;
23217                                 } else {
23218                                         $this->lMargin += $this->listindent;
23219                                         $this->x += $this->listindent;
23220                                 }
23221                                 ++$this->listindentlevel;
23222                                 if ($this->listnum == 1) {
23223                                         if ($key > 1) {
23224                                                 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
23225                                         }
23226                                 } else {
23227                                         $this->addHTMLVertSpace(0, 0, $cell, $firsttag);
23228                                 }
23229                                 break;
23230                         }
23231                         case 'li': {
23232                                 if ($key > 2) {
23233                                         $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
23234                                 }
23235                                 if ($this->listordered[$this->listnum]) {
23236                                         // ordered item
23237                                         if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) {
23238                                                 $this->lispacer = $parent['attribute']['type'];
23239                                         } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) {
23240                                                 $this->lispacer = $parent['listtype'];
23241                                         } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) {
23242                                                 $this->lispacer = $this->lisymbol;
23243                                         } else {
23244                                                 $this->lispacer = '#';
23245                                         }
23246                                         ++$this->listcount[$this->listnum];
23247                                         if (isset($tag['attribute']['value'])) {
23248                                                 $this->listcount[$this->listnum] = intval($tag['attribute']['value']);
23249                                         }
23250                                 } else {
23251                                         // unordered item
23252                                         if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) {
23253                                                 $this->lispacer = $parent['attribute']['type'];
23254                                         } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) {
23255                                                 $this->lispacer = $parent['listtype'];
23256                                         } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) {
23257                                                 $this->lispacer = $this->lisymbol;
23258                                         } else {
23259                                                 $this->lispacer = '!';
23260                                         }
23261                                 }
23262                                 break;
23263                         }
23264                         case 'blockquote': {
23265                                 if ($this->rtl) {
23266                                         $this->rMargin += $this->listindent;
23267                                 } else {
23268                                         $this->lMargin += $this->listindent;
23269                                 }
23270                                 ++$this->listindentlevel;
23271                                 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
23272                                 break;
23273                         }
23274                         case 'br': {
23275                                 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
23276                                 break;
23277                         }
23278                         case 'div': {
23279                                 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
23280                                 break;
23281                         }
23282                         case 'p': {
23283                                 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
23284                                 break;
23285                         }
23286                         case 'pre': {
23287                                 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
23288                                 $this->premode = true;
23289                                 break;
23290                         }
23291                         case 'sup': {
23292                                 $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k));
23293                                 break;
23294                         }
23295                         case 'sub': {
23296                                 $this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k));
23297                                 break;
23298                         }
23299                         case 'h1':
23300                         case 'h2':
23301                         case 'h3':
23302                         case 'h4':
23303                         case 'h5':
23304                         case 'h6': {
23305                                 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
23306                                 break;
23307                         }
23308                         // Form fields (since 4.8.000 - 2009-09-07)
23309                         case 'form': {
23310                                 if (isset($tag['attribute']['action'])) {
23311                                         $this->form_action = $tag['attribute']['action'];
23312                                 } else {
23313                                         $this->form_action = K_PATH_URL.$_SERVER['SCRIPT_NAME'];
23314                                 }
23315                                 if (isset($tag['attribute']['enctype'])) {
23316                                         $this->form_enctype = $tag['attribute']['enctype'];
23317                                 } else {
23318                                         $this->form_enctype = 'application/x-www-form-urlencoded';
23319                                 }
23320                                 if (isset($tag['attribute']['method'])) {
23321                                         $this->form_mode = $tag['attribute']['method'];
23322                                 } else {
23323                                         $this->form_mode = 'post';
23324                                 }
23325                                 break;
23326                         }
23327                         case 'input': {
23328                                 if (isset($tag['attribute']['name']) AND !$this->empty_string($tag['attribute']['name'])) {
23329                                         $name = $tag['attribute']['name'];
23330                                 } else {
23331                                         break;
23332                                 }
23333                                 $prop = array();
23334                                 $opt = array();
23335                                 if (isset($tag['attribute']['readonly']) AND !$this->empty_string($tag['attribute']['readonly'])) {
23336                                         $prop['readonly'] = true;
23337                                 }
23338                                 if (isset($tag['attribute']['value']) AND !$this->empty_string($tag['attribute']['value'])) {
23339                                         $value = $tag['attribute']['value'];
23340                                 }
23341                                 if (isset($tag['attribute']['maxlength']) AND !$this->empty_string($tag['attribute']['maxlength'])) {
23342                                         $opt['maxlen'] = intval($tag['attribute']['value']);
23343                                 }
23344                                 $h = $this->FontSize * $this->cell_height_ratio;
23345                                 if (isset($tag['attribute']['size']) AND !$this->empty_string($tag['attribute']['size'])) {
23346                                         $w = intval($tag['attribute']['size']) * $this->GetStringWidth(chr(32)) * 2;
23347                                 } else {
23348                                         $w = $h;
23349                                 }
23350                                 if (isset($tag['attribute']['checked']) AND (($tag['attribute']['checked'] == 'checked') OR ($tag['attribute']['checked'] == 'true'))) {
23351                                         $checked = true;
23352                                 } else {
23353                                         $checked = false;
23354                                 }
23355                                 if (isset($tag['align'])) {
23356                                         switch ($tag['align']) {
23357                                                 case 'C': {
23358                                                         $opt['q'] = 1;
23359                                                         break;
23360                                                 }
23361                                                 case 'R': {
23362                                                         $opt['q'] = 2;
23363                                                         break;
23364                                                 }
23365                                                 case 'L':
23366                                                 default: {
23367                                                         break;
23368                                                 }
23369                                         }
23370                                 }
23371                                 switch ($tag['attribute']['type']) {
23372                                         case 'text': {
23373                                                 if (isset($value)) {
23374                                                         $opt['v'] = $value;
23375                                                 }
23376                                                 $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
23377                                                 break;
23378                                         }
23379                                         case 'password': {
23380                                                 if (isset($value)) {
23381                                                         $opt['v'] = $value;
23382                                                 }
23383                                                 $prop['password'] = 'true';
23384                                                 $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
23385                                                 break;
23386                                         }
23387                                         case 'checkbox': {
23388                                                 $this->CheckBox($name, $w, $checked, $prop, $opt, $value, '', '', false);
23389                                                 break;
23390                                         }
23391                                         case 'radio': {
23392                                                 $this->RadioButton($name, $w, $prop, $opt, $value, $checked, '', '', false);
23393                                                 break;
23394                                         }
23395                                         case 'submit': {
23396                                                 $w = $this->GetStringWidth($value) * 1.5;
23397                                                 $h *= 1.6;
23398                                                 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
23399                                                 $action = array();
23400                                                 $action['S'] = 'SubmitForm';
23401                                                 $action['F'] = $this->form_action;
23402                                                 if ($this->form_enctype != 'FDF') {
23403                                                         $action['Flags'] = array('ExportFormat');
23404                                                 }
23405                                                 if ($this->form_mode == 'get') {
23406                                                         $action['Flags'] = array('GetMethod');
23407                                                 }
23408                                                 $this->Button($name, $w, $h, $value, $action, $prop, $opt, '', '', false);
23409                                                 break;
23410                                         }
23411                                         case 'reset': {
23412                                                 $w = $this->GetStringWidth($value) * 1.5;
23413                                                 $h *= 1.6;
23414                                                 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
23415                                                 $this->Button($name, $w, $h, $value, array('S'=>'ResetForm'), $prop, $opt, '', '', false);
23416                                                 break;
23417                                         }
23418                                         case 'file': {
23419                                                 $prop['fileSelect'] = 'true';
23420                                                 $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
23421                                                 if (!isset($value)) {
23422                                                         $value = '*';
23423                                                 }
23424                                                 $w = $this->GetStringWidth($value) * 2;
23425                                                 $h *= 1.2;
23426                                                 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
23427                                                 $jsaction = 'var f=this.getField(\''.$name.'\'); f.browseForFileToSubmit();';
23428                                                 $this->Button('FB_'.$name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
23429                                                 break;
23430                                         }
23431                                         case 'hidden': {
23432                                                 if (isset($value)) {
23433                                                         $opt['v'] = $value;
23434                                                 }
23435                                                 $opt['f'] = array('invisible', 'hidden');
23436                                                 $this->TextField($name, 0, 0, $prop, $opt, '', '', false);
23437                                                 break;
23438                                         }
23439                                         case 'image': {
23440                                                 // THIS TYPE MUST BE FIXED
23441                                                 if (isset($tag['attribute']['src']) AND !$this->empty_string($tag['attribute']['src'])) {
23442                                                         $img = $tag['attribute']['src'];
23443                                                 } else {
23444                                                         break;
23445                                                 }
23446                                                 $value = 'img';
23447                                                 //$opt['mk'] = array('i'=>$img, 'tp'=>1, 'if'=>array('sw'=>'A', 's'=>'A', 'fb'=>false));
23448                                                 if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
23449                                                         $jsaction = $tag['attribute']['onclick'];
23450                                                 } else {
23451                                                         $jsaction = '';
23452                                                 }
23453                                                 $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
23454                                                 break;
23455                                         }
23456                                         case 'button': {
23457                                                 $w = $this->GetStringWidth($value) * 1.5;
23458                                                 $h *= 1.6;
23459                                                 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
23460                                                 if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
23461                                                         $jsaction = $tag['attribute']['onclick'];
23462                                                 } else {
23463                                                         $jsaction = '';
23464                                                 }
23465                                                 $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
23466                                                 break;
23467                                         }
23468                                 }
23469                                 break;
23470                         }
23471                         case 'textarea': {
23472                                 $prop = array();
23473                                 $opt = array();
23474                                 if (isset($tag['attribute']['readonly']) AND !$this->empty_string($tag['attribute']['readonly'])) {
23475                                         $prop['readonly'] = true;
23476                                 }
23477                                 if (isset($tag['attribute']['name']) AND !$this->empty_string($tag['attribute']['name'])) {
23478                                         $name = $tag['attribute']['name'];
23479                                 } else {
23480                                         break;
23481                                 }
23482                                 if (isset($tag['attribute']['value']) AND !$this->empty_string($tag['attribute']['value'])) {
23483                                         $opt['v'] = $tag['attribute']['value'];
23484                                 }
23485                                 if (isset($tag['attribute']['cols']) AND !$this->empty_string($tag['attribute']['cols'])) {
23486                                         $w = intval($tag['attribute']['cols']) * $this->GetStringWidth(chr(32)) * 2;
23487                                 } else {
23488                                         $w = 40;
23489                                 }
23490                                 if (isset($tag['attribute']['rows']) AND !$this->empty_string($tag['attribute']['rows'])) {
23491                                         $h = intval($tag['attribute']['rows']) * $this->FontSize * $this->cell_height_ratio;
23492                                 } else {
23493                                         $h = 10;
23494                                 }
23495                                 $prop['multiline'] = 'true';
23496                                 $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
23497                                 break;
23498                         }
23499                         case 'select': {
23500                                 $h = $this->FontSize * $this->cell_height_ratio;
23501                                 if (isset($tag['attribute']['size']) AND !$this->empty_string($tag['attribute']['size'])) {
23502                                         $h *= ($tag['attribute']['size'] + 1);
23503                                 }
23504                                 $prop = array();
23505                                 $opt = array();
23506                                 if (isset($tag['attribute']['name']) AND !$this->empty_string($tag['attribute']['name'])) {
23507                                         $name = $tag['attribute']['name'];
23508                                 } else {
23509                                         break;
23510                                 }
23511                                 $w = 0;
23512                                 if (isset($tag['attribute']['opt']) AND !$this->empty_string($tag['attribute']['opt'])) {
23513                                         $options = explode('#!NwL!#', $tag['attribute']['opt']);
23514                                         $values = array();
23515                                         foreach ($options as $val) {
23516                                                 if (strpos($val, '#!TaB!#') !== false) {
23517                                                         $opts = explode('#!TaB!#', $val);
23518                                                         $values[] = $opts;
23519                                                         $w = max($w, $this->GetStringWidth($opts[1]));
23520                                                 } else {
23521                                                         $values[] = $val;
23522                                                         $w = max($w, $this->GetStringWidth($val));
23523                                                 }
23524                                         }
23525                                 } else {
23526                                         break;
23527                                 }
23528                                 $w *= 2;
23529                                 if (isset($tag['attribute']['multiple']) AND ($tag['attribute']['multiple']='multiple')) {
23530                                         $prop['multipleSelection'] = 'true';
23531                                         $this->ListBox($name, $w, $h, $values, $prop, $opt, '', '', false);
23532                                 } else {
23533                                         $this->ComboBox($name, $w, $h, $values, $prop, $opt, '', '', false);
23534                                 }
23535                                 break;
23536                         }
23537                         case 'tcpdf': {
23538                                 if (defined('K_TCPDF_CALLS_IN_HTML') AND (K_TCPDF_CALLS_IN_HTML === true)) {
23539                                         // Special tag used to call TCPDF methods
23540                                         if (isset($tag['attribute']['method'])) {
23541                                                 $tcpdf_method = $tag['attribute']['method'];
23542                                                 if (method_exists($this, $tcpdf_method)) {
23543                                                         if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) {
23544                                                                 $params = unserialize(urldecode($tag['attribute']['params']));
23545                                                                 call_user_func_array(array($this, $tcpdf_method), $params);
23546                                                         } else {
23547                                                                 $this->$tcpdf_method();
23548                                                         }
23549                                                         $this->newline = true;
23550                                                 }
23551                                         }
23552                                 }
23553                                 break;
23554                         }
23555                         default: {
23556                                 break;
23557                         }
23558                 }
23559                 // define tags that support borders and background colors
23560                 $bordertags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table');
23561                 if (in_array($tag['value'], $bordertags)) {
23562                         // set border
23563                         $dom[$key]['borderposition'] = $this->getBorderStartPosition();
23564                 }
23565                 if ($dom[$key]['self'] AND isset($dom[$key]['attribute']['pagebreakafter'])) {
23566                         $pba = $dom[$key]['attribute']['pagebreakafter'];
23567                         // check for pagebreak
23568                         if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
23569                                 // add a page (or trig AcceptPageBreak() for multicolumn mode)
23570                                 $this->checkPageBreak($this->PageBreakTrigger + 1);
23571                         }
23572                         if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
23573                                 OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
23574                                 // add a page (or trig AcceptPageBreak() for multicolumn mode)
23575                                 $this->checkPageBreak($this->PageBreakTrigger + 1);
23576                         }
23577                 }
23578                 return $dom;
23579         }
23580 
23590         protected function closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney=0) {
23591                 $tag = $dom[$key];
23592                 $parent = $dom[($dom[$key]['parent'])];
23593                 $lasttag = ((!isset($dom[($key + 1)])) OR ((!isset($dom[($key + 2)])) AND ($dom[($key + 1)]['value'] == 'marker')));
23594                 $in_table_head = false;
23595                 // maximum x position (used to draw borders)
23596                 if ($this->rtl) {
23597                         $xmax = $this->w;
23598                 } else {
23599                         $xmax = 0;
23600                 }
23601                 if ($tag['block']) {
23602                         $hbz = 0; // distance from y to line bottom
23603                         $hb = 0; // vertical space between block tags
23604                         // calculate vertical space for block tags
23605                         if (isset($this->tagvspaces[$tag['value']][1]['h']) AND ($this->tagvspaces[$tag['value']][1]['h'] >= 0)) {
23606                                 $pre_h = $this->tagvspaces[$tag['value']][1]['h'];
23607                         } elseif (isset($parent['fontsize'])) {
23608                                 $pre_h = (($parent['fontsize'] / $this->k) * $this->cell_height_ratio);
23609                         } else {
23610                                 $pre_h = $this->FontSize * $this->cell_height_ratio;
23611                         }
23612                         if (isset($this->tagvspaces[$tag['value']][1]['n'])) {
23613                                 $n = $this->tagvspaces[$tag['value']][1]['n'];
23614                         } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
23615                                 $n = 0.6;
23616                         } else {
23617                                 $n = 1;
23618                         }
23619                         if ((!isset($this->tagvspaces[$tag['value']])) AND ($tag['value'] == 'div')) {
23620                                 $hb = 0;
23621                         } else {
23622                                 $hb = ($n * $pre_h);
23623                         }
23624                         if ($maxbottomliney > $this->PageBreakTrigger) {
23625                                 $hbz = ($this->FontSize * $this->cell_height_ratio);
23626                         } elseif ($this->y < $maxbottomliney) {
23627                                 $hbz = ($maxbottomliney - $this->y);
23628                         }
23629                 }
23630                 // Closing tag
23631                 switch($tag['value']) {
23632                         case 'tr': {
23633                                 $table_el = $dom[($dom[$key]['parent'])]['parent'];
23634                                 if (!isset($parent['endy'])) {
23635                                         $dom[($dom[$key]['parent'])]['endy'] = $this->y;
23636                                         $parent['endy'] = $this->y;
23637                                 }
23638                                 if (!isset($parent['endpage'])) {
23639                                         $dom[($dom[$key]['parent'])]['endpage'] = $this->page;
23640                                         $parent['endpage'] = $this->page;
23641                                 }
23642                                 if (!isset($parent['endcolumn'])) {
23643                                         $dom[($dom[$key]['parent'])]['endcolumn'] = $this->current_column;
23644                                         $parent['endcolumn'] = $this->current_column;
23645                                 }
23646                                 // update row-spanned cells
23647                                 if (isset($dom[$table_el]['rowspans'])) {
23648                                         foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
23649                                                 $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1;
23650                                                 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
23651                                                         if (($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) AND ($dom[$table_el]['rowspans'][$k]['endcolumn'] == $parent['endcolumn'])) {
23652                                                                 $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']);
23653                                                         } elseif (($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) OR ($dom[$table_el]['rowspans'][$k]['endcolumn'] > $parent['endcolumn'])) {
23654                                                                 $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
23655                                                                 $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
23656                                                                 $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
23657                                                         }
23658                                                 }
23659                                         }
23660                                         // report new endy and endpage to the rowspanned cells
23661                                         foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
23662                                                 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
23663                                                         $dom[$table_el]['rowspans'][$k]['endpage'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']);
23664                                                         $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
23665                                                         $dom[$table_el]['rowspans'][$k]['endcolumn'] = max($dom[$table_el]['rowspans'][$k]['endcolumn'], $dom[($dom[$key]['parent'])]['endcolumn']);
23666                                                         $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
23667                                                         $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']);
23668                                                         $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
23669                                                 }
23670                                         }
23671                                         // update remaining rowspanned cells
23672                                         foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
23673                                                 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
23674                                                         $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage'];
23675                                                         $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[($dom[$key]['parent'])]['endcolumn'];
23676                                                         $dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy'];
23677                                                 }
23678                                         }
23679                                 }
23680                                 $this->setPage($dom[($dom[$key]['parent'])]['endpage']);
23681                                 if ($this->num_columns > 1) {
23682                                         $this->selectColumn($dom[($dom[$key]['parent'])]['endcolumn']);
23683                                 }
23684                                 $this->y = $dom[($dom[$key]['parent'])]['endy'];
23685                                 if (isset($dom[$table_el]['attribute']['cellspacing'])) {
23686                                         $this->y += $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px');
23687                                 } elseif (isset($dom[$table_el]['border-spacing'])) {
23688                                         $this->y += $dom[$table_el]['border-spacing']['V'];
23689                                 }
23690                                 $this->Ln(0, $cell);
23691                                 if ($this->current_column == $parent['startcolumn']) {
23692                                         $this->x = $parent['startx'];
23693                                 }
23694                                 // account for booklet mode
23695                                 if ($this->page > $parent['startpage']) {
23696                                         if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$parent['startpage']]['orm'])) {
23697                                                 $this->x -= ($this->pagedim[$this->page]['orm'] - $this->pagedim[$parent['startpage']]['orm']);
23698                                         } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$parent['startpage']]['olm'])) {
23699                                                 $this->x += ($this->pagedim[$this->page]['olm'] - $this->pagedim[$parent['startpage']]['olm']);
23700                                         }
23701                                 }
23702                                 break;
23703                         }
23704                         case 'tablehead':
23705                                 // closing tag used for the thead part
23706                                 $in_table_head = true;
23707                                 $this->inthead = false;
23708                         case 'table': {
23709                                 $table_el = $parent;
23710                                 // set default border
23711                                 if (isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0)) {
23712                                         // set default border
23713                                         $border = array('LTRB' => array('width' => $this->getCSSBorderWidth($table_el['attribute']['border']), 'cap'=>'square', 'join'=>'miter', 'dash'=> 0, 'color'=>array(0,0,0)));
23714                                 } else {
23715                                         $border = 0;
23716                                 }
23717                                 $default_border = $border;
23718                                 // fix bottom line alignment of last line before page break
23719                                 foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) {
23720                                         // update row-spanned cells
23721                                         if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
23722                                                 foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
23723                                                         if ($trwsp['trid'] == $trkey) {
23724                                                                 $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1;
23725                                                         }
23726                                                         if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] >= 0)) {
23727                                                                 $dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey;
23728                                                         }
23729                                                 }
23730                                         }
23731                                         if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) {
23732                                                 $pgendy = $this->pagedim[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim[$dom[$prevtrkey]['endpage']]['bm'];
23733                                                 $dom[$prevtrkey]['endy'] = $pgendy;
23734                                                 // update row-spanned cells
23735                                                 if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
23736                                                         foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
23737                                                                 if (($trwsp['trid'] == $trkey) AND ($trwsp['mrowspan'] > 1) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) {
23738                                                                         $dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy;
23739                                                                         $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1;
23740                                                                 }
23741                                                         }
23742                                                 }
23743                                         }
23744                                         $prevtrkey = $trkey;
23745                                         $table_el = $dom[($dom[$key]['parent'])];
23746                                 }
23747                                 // for each row
23748                                 if (count($table_el['trids']) > 0) {
23749                                         unset($xmax);
23750                                 }
23751                                 foreach ($table_el['trids'] as $j => $trkey) {
23752                                         $parent = $dom[$trkey];
23753                                         if (!isset($xmax)) {
23754                                                 $xmax = $parent['cellpos'][(count($parent['cellpos']) - 1)]['endx'];
23755                                         }
23756                                         // for each cell on the row
23757                                         foreach ($parent['cellpos'] as $k => $cellpos) {
23758                                                 if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) {
23759                                                         $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx'];
23760                                                         $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx'];
23761                                                         $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy'];
23762                                                         $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage'];
23763                                                         $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage'];
23764                                                         $startcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['startcolumn'];
23765                                                         $endcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['endcolumn'];
23766                                                 } else {
23767                                                         $endy = $parent['endy'];
23768                                                         $startpage = $parent['startpage'];
23769                                                         $endpage = $parent['endpage'];
23770                                                         $startcolumn = $parent['startcolumn'];
23771                                                         $endcolumn = $parent['endcolumn'];
23772                                                 }
23773                                                 if ($this->num_columns == 0) {
23774                                                         $this->num_columns = 1;
23775                                                 }
23776                                                 if (isset($cellpos['border'])) {
23777                                                         $border = $cellpos['border'];
23778                                                 }
23779                                                 if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
23780                                                         $this->SetFillColorArray($cellpos['bgcolor']);
23781                                                         $fill = true;
23782                                                 } else {
23783                                                         $fill = false;
23784                                                 }
23785                                                 $x = $cellpos['startx'];
23786                                                 $y = $parent['starty'];
23787                                                 $starty = $y;
23788                                                 $w = abs($cellpos['endx'] - $cellpos['startx']);
23789                                                 // get border modes
23790                                                 $border_start = $this->getBorderMode($border, $position='start');
23791                                                 $border_end = $this->getBorderMode($border, $position='end');
23792                                                 $border_middle = $this->getBorderMode($border, $position='middle');
23793                                                 // design borders around HTML cells.
23794                                                 for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
23795                                                         $ccode = '';
23796                                                         $this->setPage($page);
23797                                                         if ($this->num_columns < 2) {
23798                                                                 // single-column mode
23799                                                                 $this->x = $x;
23800                                                                 $this->y = $this->tMargin;
23801                                                         }
23802                                                         // account for margin changes
23803                                                         if ($page > $startpage) {
23804                                                                 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
23805                                                                         $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
23806                                                                 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
23807                                                                         $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
23808                                                                 }
23809                                                         }
23810                                                         if ($startpage == $endpage) { // single page
23811                                                                 $deltacol = 0;
23812                                                                 $deltath = 0;
23813                                                                 for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
23814                                                                         $this->selectColumn($column);
23815                                                                         if ($startcolumn == $endcolumn) { // single column
23816                                                                                 $cborder = $border;
23817                                                                                 $h = $endy - $parent['starty'];
23818                                                                                 $this->y = $y;
23819                                                                                 $this->x = $x;
23820                                                                         } elseif ($column == $startcolumn) { // first column
23821                                                                                 $cborder = $border_start;
23822                                                                                 $this->y = $starty;
23823                                                                                 $this->x = $x;
23824                                                                                 $h = $this->h - $this->y - $this->bMargin;
23825                                                                                 if ($this->rtl) {
23826                                                                                         $deltacol = $this->x + $this->rMargin - $this->w;
23827                                                                                 } else {
23828                                                                                         $deltacol = $this->x - $this->lMargin;
23829                                                                                 }
23830                                                                         } elseif ($column == $endcolumn) { // end column
23831                                                                                 $cborder = $border_end;
23832                                                                                 if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
23833                                                                                         $this->y = $this->columns[$column]['th']['\''.$page.'\''];
23834                                                                                 }
23835                                                                                 $this->x += $deltacol;
23836                                                                                 $h = $endy - $this->y;
23837                                                                         } else { // middle column
23838                                                                                 $cborder = $border_middle;
23839                                                                                 if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
23840                                                                                         $this->y = $this->columns[$column]['th']['\''.$page.'\''];
23841                                                                                 }
23842                                                                                 $this->x += $deltacol;
23843                                                                                 $h = $this->h - $this->y - $this->bMargin;
23844                                                                         }
23845                                                                         $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
23846                                                                 } // end for each column
23847                                                         } elseif ($page == $startpage) { // first page
23848                                                                 $deltacol = 0;
23849                                                                 $deltath = 0;
23850                                                                 for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
23851                                                                         $this->selectColumn($column);
23852                                                                         if ($column == $startcolumn) { // first column
23853                                                                                 $cborder = $border_start;
23854                                                                                 $this->y = $starty;
23855                                                                                 $this->x = $x;
23856                                                                                 $h = $this->h - $this->y - $this->bMargin;
23857                                                                                 if ($this->rtl) {
23858                                                                                         $deltacol = $this->x + $this->rMargin - $this->w;
23859                                                                                 } else {
23860                                                                                         $deltacol = $this->x - $this->lMargin;
23861                                                                                 }
23862                                                                         } else { // middle column
23863                                                                                 $cborder = $border_middle;
23864                                                                                 if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
23865                                                                                         $this->y = $this->columns[$column]['th']['\''.$page.'\''];
23866                                                                                 }
23867                                                                                 $this->x += $deltacol;
23868                                                                                 $h = $this->h - $this->y - $this->bMargin;
23869                                                                         }
23870                                                                         $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
23871                                                                 } // end for each column
23872                                                         } elseif ($page == $endpage) { // last page
23873                                                                 $deltacol = 0;
23874                                                                 $deltath = 0;
23875                                                                 for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
23876                                                                         $this->selectColumn($column);
23877                                                                         if ($column == $endcolumn) { // end column
23878                                                                                 $cborder = $border_end;
23879                                                                                 if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
23880                                                                                         $this->y = $this->columns[$column]['th']['\''.$page.'\''];
23881                                                                                 }
23882                                                                                 $this->x += $deltacol;
23883                                                                                 $h = $endy - $this->y;
23884                                                                         } else { // middle column
23885                                                                                 $cborder = $border_middle;
23886                                                                                 if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
23887                                                                                         $this->y = $this->columns[$column]['th']['\''.$page.'\''];
23888                                                                                 }
23889                                                                                 $this->x += $deltacol;
23890                                                                                 $h = $this->h - $this->y - $this->bMargin;
23891                                                                         }
23892                                                                         $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
23893                                                                 } // end for each column
23894                                                         } else { // middle page
23895                                                                 $deltacol = 0;
23896                                                                 $deltath = 0;
23897                                                                 for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
23898                                                                         $this->selectColumn($column);
23899                                                                         $cborder = $border_middle;
23900                                                                         if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
23901                                                                                 $this->y = $this->columns[$column]['th']['\''.$page.'\''];
23902                                                                         }
23903                                                                         $this->x += $deltacol;
23904                                                                         $h = $this->h - $this->y - $this->bMargin;
23905                                                                         $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
23906                                                                 } // end for each column
23907                                                         }
23908                                                         if ($cborder OR $fill) {
23909                                                                 $offsetlen = strlen($ccode);
23910                                                                 // draw border and fill
23911                                                                 if ($this->inxobj) {
23912                                                                         // we are inside an XObject template
23913                                                                         if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
23914                                                                                 $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
23915                                                                                 $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
23916                                                                                 $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
23917                                                                         } else {
23918                                                                                 $pagemark = $this->xobjects[$this->xobjid]['intmrk'];
23919                                                                                 $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
23920                                                                         }
23921                                                                         $pagebuff = $this->xobjects[$this->xobjid]['outdata'];
23922                                                                         $pstart = substr($pagebuff, 0, $pagemark);
23923                                                                         $pend = substr($pagebuff, $pagemark);
23924                                                                         $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
23925                                                                 } else {
23926                                                                         // draw border and fill
23927                                                                         if (end($this->transfmrk[$this->page]) !== false) {
23928                                                                                 $pagemarkkey = key($this->transfmrk[$this->page]);
23929                                                                                 $pagemark = $this->transfmrk[$this->page][$pagemarkkey];
23930                                                                                 $this->transfmrk[$this->page][$pagemarkkey] += $offsetlen;
23931                                                                         } elseif ($this->InFooter) {
23932                                                                                 $pagemark = $this->footerpos[$this->page];
23933                                                                                 $this->footerpos[$this->page] += $offsetlen;
23934                                                                         } else {
23935                                                                                 $pagemark = $this->intmrk[$this->page];
23936                                                                                 $this->intmrk[$this->page] += $offsetlen;
23937                                                                         }
23938                                                                         $pagebuff = $this->getPageBuffer($this->page);
23939                                                                         $pstart = substr($pagebuff, 0, $pagemark);
23940                                                                         $pend = substr($pagebuff, $pagemark);
23941                                                                         $this->setPageBuffer($this->page, $pstart.$ccode.$pend);
23942                                                                 }
23943                                                         }
23944                                                 } // end for each page
23945                                                 // restore default border
23946                                                 $border = $default_border;
23947                                         } // end for each cell on the row
23948                                         if (isset($table_el['attribute']['cellspacing'])) {
23949                                                 $this->y += $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px');
23950                                         } elseif (isset($table_el['border-spacing'])) {
23951                                                 $this->y += $table_el['border-spacing']['V'];
23952                                         }
23953                                         $this->Ln(0, $cell);
23954                                         $this->x = $parent['startx'];
23955                                         if ($endpage > $startpage) {
23956                                                 if (($this->rtl) AND ($this->pagedim[$endpage]['orm'] != $this->pagedim[$startpage]['orm'])) {
23957                                                         $this->x += ($this->pagedim[$endpage]['orm'] - $this->pagedim[$startpage]['orm']);
23958                                                 } elseif ((!$this->rtl) AND ($this->pagedim[$endpage]['olm'] != $this->pagedim[$startpage]['olm'])) {
23959                                                         $this->x += ($this->pagedim[$endpage]['olm'] - $this->pagedim[$startpage]['olm']);
23960                                                 }
23961                                         }
23962                                 }
23963                                 if (!$in_table_head) { // we are not inside a thead section
23964                                         $this->cell_padding = $table_el['old_cell_padding'];
23965                                         // reset row height
23966                                         $this->resetLastH();
23967                                         if (($this->page == ($this->numpages - 1)) AND ($this->pageopen[$this->numpages])) {
23968                                                 $plendiff = ($this->pagelen[$this->numpages] - $this->emptypagemrk[$this->numpages]);
23969                                                 if (($plendiff > 0) AND ($plendiff < 60)) {
23970                                                         $pagediff = substr($this->getPageBuffer($this->numpages), $this->emptypagemrk[$this->numpages], $plendiff);
23971                                                         if (substr($pagediff, 0, 5) == 'BT /F') {
23972                                                                 // the difference is only a font setting
23973                                                                 $plendiff = 0;
23974                                                         }
23975                                                 }
23976                                                 if ($plendiff == 0) {
23977                                                         // remove last blank page
23978                                                         $this->deletePage($this->numpages);
23979                                                 }
23980                                         }
23981                                         if (isset($this->theadMargins['top'])) {
23982                                                 // restore top margin
23983                                                 $this->tMargin = $this->theadMargins['top'];
23984                                         }
23985                                         if (!isset($table_el['attribute']['nested']) OR ($table_el['attribute']['nested'] != 'true')) {
23986                                                 // reset main table header
23987                                                 $this->thead = '';
23988                                                 $this->theadMargins = array();
23989                                                 $this->pagedim[$this->page]['tm'] = $this->tMargin;
23990                                         }
23991                                 }
23992                                 $parent = $table_el;
23993                                 break;
23994                         }
23995                         case 'a': {
23996                                 $this->HREF = '';
23997                                 break;
23998                         }
23999                         case 'sup': {
24000                                 $this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k));
24001                                 break;
24002                         }
24003                         case 'sub': {
24004                                 $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize'])/$this->k));
24005                                 break;
24006                         }
24007                         case 'div': {
24008                                 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
24009                                 break;
24010                         }
24011                         case 'blockquote': {
24012                                 if ($this->rtl) {
24013                                         $this->rMargin -= $this->listindent;
24014                                 } else {
24015                                         $this->lMargin -= $this->listindent;
24016                                 }
24017                                 --$this->listindentlevel;
24018                                 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
24019                                 break;
24020                         }
24021                         case 'p': {
24022                                 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
24023                                 break;
24024                         }
24025                         case 'pre': {
24026                                 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
24027                                 $this->premode = false;
24028                                 break;
24029                         }
24030                         case 'dl': {
24031                                 --$this->listnum;
24032                                 if ($this->listnum <= 0) {
24033                                         $this->listnum = 0;
24034                                         $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
24035                                 } else {
24036                                         $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
24037                                 }
24038                                 $this->resetLastH();
24039                                 break;
24040                         }
24041                         case 'dt': {
24042                                 $this->lispacer = '';
24043                                 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
24044                                 break;
24045                         }
24046                         case 'dd': {
24047                                 $this->lispacer = '';
24048                                 if ($this->rtl) {
24049                                         $this->rMargin -= $this->listindent;
24050                                 } else {
24051                                         $this->lMargin -= $this->listindent;
24052                                 }
24053                                 --$this->listindentlevel;
24054                                 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
24055                                 break;
24056                         }
24057                         case 'ul':
24058                         case 'ol': {
24059                                 --$this->listnum;
24060                                 $this->lispacer = '';
24061                                 if ($this->rtl) {
24062                                         $this->rMargin -= $this->listindent;
24063                                 } else {
24064                                         $this->lMargin -= $this->listindent;
24065                                 }
24066                                 --$this->listindentlevel;
24067                                 if ($this->listnum <= 0) {
24068                                         $this->listnum = 0;
24069                                         $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
24070                                 } else {
24071                                         $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
24072                                 }
24073                                 $this->resetLastH();
24074                                 break;
24075                         }
24076                         case 'li': {
24077                                 $this->lispacer = '';
24078                                 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
24079                                 break;
24080                         }
24081                         case 'h1':
24082                         case 'h2':
24083                         case 'h3':
24084                         case 'h4':
24085                         case 'h5':
24086                         case 'h6': {
24087                                 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
24088                                 break;
24089                         }
24090                         // Form fields (since 4.8.000 - 2009-09-07)
24091                         case 'form': {
24092                                 $this->form_action = '';
24093                                 $this->form_enctype = 'application/x-www-form-urlencoded';
24094                                 break;
24095                         }
24096                         default : {
24097                                 break;
24098                         }
24099                 }
24100                 // draw border and background (if any)
24101                 $this->drawHTMLTagBorder($parent, $xmax);
24102                 if (isset($dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'])) {
24103                         $pba = $dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'];
24104                         // check for pagebreak
24105                         if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
24106                                 // add a page (or trig AcceptPageBreak() for multicolumn mode)
24107                                 $this->checkPageBreak($this->PageBreakTrigger + 1);
24108                         }
24109                         if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
24110                                 OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
24111                                 // add a page (or trig AcceptPageBreak() for multicolumn mode)
24112                                 $this->checkPageBreak($this->PageBreakTrigger + 1);
24113                         }
24114                 }
24115                 $this->tmprtl = false;
24116                 return $dom;
24117         }
24118 
24128         protected function addHTMLVertSpace($hbz=0, $hb=0, $cell=false, $firsttag=false, $lasttag=false) {
24129                 if ($firsttag) {
24130                         $this->Ln(0, $cell);
24131                         $this->htmlvspace = 0;
24132                         return;
24133                 }
24134                 if ($lasttag) {
24135                         $this->Ln($hbz, $cell);
24136                         $this->htmlvspace = 0;
24137                         return;
24138                 }
24139                 if ($hb < $this->htmlvspace) {
24140                         $hd = 0;
24141                 } else {
24142                         $hd = $hb - $this->htmlvspace;
24143                         $this->htmlvspace = $hb;
24144                 }
24145                 $this->Ln(($hbz + $hd), $cell);
24146         }
24147 
24154         protected function getBorderStartPosition() {
24155                 if ($this->rtl) {
24156                         $xmax = $this->lMargin;
24157                 } else {
24158                         $xmax = $this->w - $this->rMargin;
24159                 }
24160                 return array('page' => $this->page, 'column' => $this->current_column, 'x' => $this->x, 'y' => $this->y, 'xmax' => $xmax);
24161         }
24162 
24170         protected function drawHTMLTagBorder($tag, $xmax) {
24171                 if (!isset($tag['borderposition'])) {
24172                         // nothing to draw
24173                         return;
24174                 }
24175                 $prev_x = $this->x;
24176                 $prev_y = $this->y;
24177                 $prev_lasth = $this->lasth;
24178                 $border = 0;
24179                 $fill = false;
24180                 $this->lasth = 0;
24181                 if (isset($tag['border']) AND !empty($tag['border'])) {
24182                         // get border style
24183                         $border = $tag['border'];
24184                         if (!$this->empty_string($this->thead) AND (!$this->inthead)) {
24185                                 // border for table header
24186                                 $border = $this->getBorderMode($border, $position='middle');
24187                         }
24188                 }
24189                 if (isset($tag['bgcolor']) AND ($tag['bgcolor'] !== false)) {
24190                         // get background color
24191                         $old_bgcolor = $this->bgcolor;
24192                         $this->SetFillColorArray($tag['bgcolor']);
24193                         $fill = true;
24194                 }
24195                 if (!$border AND !$fill) {
24196                         // nothing to draw
24197                         return;
24198                 }
24199                 if (isset($tag['attribute']['cellspacing'])) {
24200                         $clsp = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
24201                         $cellspacing = array('H' => $clsp, 'V' => $clsp);
24202                 } elseif (isset($tag['border-spacing'])) {
24203                         $cellspacing = $tag['border-spacing'];
24204                 } else {
24205                         $cellspacing = array('H' => 0, 'V' => 0);
24206                 }
24207                 if (($tag['value'] != 'table') AND (is_array($border)) AND (!empty($border))) {
24208                         // draw the border externally respect the sqare edge.
24209                         $border['mode'] = 'ext';
24210                 }
24211                 if ($this->rtl) {
24212                         if ($xmax >= $tag['borderposition']['x']) {
24213                                 $xmax = $tag['borderposition']['xmax'];
24214                         }
24215                         $w = ($tag['borderposition']['x'] - $xmax);
24216                 } else {
24217                         if ($xmax <= $tag['borderposition']['x']) {
24218                                 $xmax = $tag['borderposition']['xmax'];
24219                         }
24220                         $w = ($xmax - $tag['borderposition']['x']);
24221                 }
24222                 if ($w <= 0) {
24223                         return;
24224                 }
24225                 $w += $cellspacing['H'];
24226                 $startpage = $tag['borderposition']['page'];
24227                 $startcolumn = $tag['borderposition']['column'];
24228                 $x = $tag['borderposition']['x'];
24229                 $y = $tag['borderposition']['y'];
24230                 $endpage = $this->page;
24231                 $starty = $tag['borderposition']['y'] - $cellspacing['V'];
24232                 $currentY = $this->y;
24233                 $this->x = $x;
24234                 // get latest column
24235                 $endcolumn = $this->current_column;
24236                 if ($this->num_columns == 0) {
24237                         $this->num_columns = 1;
24238                 }
24239                 // get border modes
24240                 $border_start = $this->getBorderMode($border, $position='start');
24241                 $border_end = $this->getBorderMode($border, $position='end');
24242                 $border_middle = $this->getBorderMode($border, $position='middle');
24243                 // temporary disable page regions
24244                 $temp_page_regions = $this->page_regions;
24245                 $this->page_regions = array();
24246                 // design borders around HTML cells.
24247                 for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
24248                         $ccode = '';
24249                         $this->setPage($page);
24250                         if ($this->num_columns < 2) {
24251                                 // single-column mode
24252                                 $this->x = $x;
24253                                 $this->y = $this->tMargin;
24254                         }
24255                         // account for margin changes
24256                         if ($page > $startpage) {
24257                                 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
24258                                         $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
24259                                 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
24260                                         $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
24261                                 }
24262                         }
24263                         if ($startpage == $endpage) {
24264                                 // single page
24265                                 for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
24266                                         $this->selectColumn($column);
24267                                         if ($startcolumn == $endcolumn) { // single column
24268                                                 $cborder = $border;
24269                                                 $h = ($currentY - $y) + $cellspacing['V'];
24270                                                 $this->y = $starty;
24271                                         } elseif ($column == $startcolumn) { // first column
24272                                                 $cborder = $border_start;
24273                                                 $this->y = $starty;
24274                                                 $h = $this->h - $this->y - $this->bMargin;
24275                                         } elseif ($column == $endcolumn) { // end column
24276                                                 $cborder = $border_end;
24277                                                 $h = $currentY - $this->y;
24278                                         } else { // middle column
24279                                                 $cborder = $border_middle;
24280                                                 $h = $this->h - $this->y - $this->bMargin;
24281                                         }
24282                                         $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
24283                                 } // end for each column
24284                         } elseif ($page == $startpage) { // first page
24285                                 for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
24286                                         $this->selectColumn($column);
24287                                         if ($column == $startcolumn) { // first column
24288                                                 $cborder = $border_start;
24289                                                 $this->y = $starty;
24290                                                 $h = $this->h - $this->y - $this->bMargin;
24291                                         } else { // middle column
24292                                                 $cborder = $border_middle;
24293                                                 $h = $this->h - $this->y - $this->bMargin;
24294                                         }
24295                                         $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
24296                                 } // end for each column
24297                         } elseif ($page == $endpage) { // last page
24298                                 for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
24299                                         $this->selectColumn($column);
24300                                         if ($column == $endcolumn) {
24301                                                 // end column
24302                                                 $cborder = $border_end;
24303                                                 $h = $currentY - $this->y;
24304                                         } else {
24305                                                 // middle column
24306                                                 $cborder = $border_middle;
24307                                                 $h = $this->h - $this->y - $this->bMargin;
24308                                         }
24309                                         $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
24310                                 } // end for each column
24311                         } else { // middle page
24312                                 for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
24313                                         $this->selectColumn($column);
24314                                         $cborder = $border_middle;
24315                                         $h = $this->h - $this->y - $this->bMargin;
24316                                         $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
24317                                 } // end for each column
24318                         }
24319                         if ($cborder OR $fill) {
24320                                 $offsetlen = strlen($ccode);
24321                                 // draw border and fill
24322                                 if ($this->inxobj) {
24323                                         // we are inside an XObject template
24324                                         if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
24325                                                 $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
24326                                                 $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
24327                                                 $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
24328                                         } else {
24329                                                 $pagemark = $this->xobjects[$this->xobjid]['intmrk'];
24330                                                 $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
24331                                         }
24332                                         $pagebuff = $this->xobjects[$this->xobjid]['outdata'];
24333                                         $pstart = substr($pagebuff, 0, $pagemark);
24334                                         $pend = substr($pagebuff, $pagemark);
24335                                         $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
24336                                 } else {
24337                                         if (end($this->transfmrk[$this->page]) !== false) {
24338                                                 $pagemarkkey = key($this->transfmrk[$this->page]);
24339                                                 $pagemark = $this->transfmrk[$this->page][$pagemarkkey];
24340                                                 $this->transfmrk[$this->page][$pagemarkkey] += $offsetlen;
24341                                         } elseif ($this->InFooter) {
24342                                                 $pagemark = $this->footerpos[$this->page];
24343                                                 $this->footerpos[$this->page] += $offsetlen;
24344                                         } else {
24345                                                 $pagemark = $this->intmrk[$this->page];
24346                                                 $this->intmrk[$this->page] += $offsetlen;
24347                                         }
24348                                         $pagebuff = $this->getPageBuffer($this->page);
24349                                         $pstart = substr($pagebuff, 0, $this->bordermrk[$this->page]);
24350                                         $pend = substr($pagebuff, $this->bordermrk[$this->page]);
24351                                         $this->setPageBuffer($this->page, $pstart.$ccode.$pend);
24352                                         $this->bordermrk[$this->page] += $offsetlen;
24353                                         $this->cntmrk[$this->page] += $offsetlen;
24354                                 }
24355                         }
24356                 } // end for each page
24357                 // restore page regions
24358                 $this->page_regions = $temp_page_regions;
24359                 if (isset($old_bgcolor)) {
24360                         // restore background color
24361                         $this->SetFillColorArray($old_bgcolor);
24362                 }
24363                 // restore pointer position
24364                 $this->x = $prev_x;
24365                 $this->y = $prev_y;
24366                 $this->lasth = $prev_lasth;
24367         }
24368 
24375         public function setLIsymbol($symbol='!') {
24376                 // check for custom image symbol
24377                 if (substr($symbol, 0, 4) == 'img|') {
24378                         $this->lisymbol = $symbol;
24379                         return;
24380                 }
24381                 $symbol = strtolower($symbol);
24382                 switch ($symbol) {
24383                         case '!' :
24384                         case '#' :
24385                         case 'disc' :
24386                         case 'circle' :
24387                         case 'square' :
24388                         case '1':
24389                         case 'decimal':
24390                         case 'decimal-leading-zero':
24391                         case 'i':
24392                         case 'lower-roman':
24393                         case 'I':
24394                         case 'upper-roman':
24395                         case 'a':
24396                         case 'lower-alpha':
24397                         case 'lower-latin':
24398                         case 'A':
24399                         case 'upper-alpha':
24400                         case 'upper-latin':
24401                         case 'lower-greek': {
24402                                 $this->lisymbol = $symbol;
24403                                 break;
24404                         }
24405                         default : {
24406                                 $this->lisymbol = '';
24407                         }
24408                 }
24409         }
24410 
24419         public function SetBooklet($booklet=true, $inner=-1, $outer=-1) {
24420                 $this->booklet = $booklet;
24421                 if ($inner >= 0) {
24422                         $this->lMargin = $inner;
24423                 }
24424                 if ($outer >= 0) {
24425                         $this->rMargin = $outer;
24426                 }
24427         }
24428 
24435         protected function swapMargins($reverse=true) {
24436                 if ($reverse) {
24437                         // swap left and right margins
24438                         $mtemp = $this->original_lMargin;
24439                         $this->original_lMargin = $this->original_rMargin;
24440                         $this->original_rMargin = $mtemp;
24441                         $deltam = $this->original_lMargin - $this->original_rMargin;
24442                         $this->lMargin += $deltam;
24443                         $this->rMargin -= $deltam;
24444                 }
24445         }
24446 
24459         public function setHtmlVSpace($tagvs) {
24460                 $this->tagvspaces = $tagvs;
24461         }
24462 
24469         public function setListIndentWidth($width) {
24470                 return $this->customlistindent = floatval($width);
24471         }
24472 
24479         public function setOpenCell($isopen) {
24480                 $this->opencell = $isopen;
24481         }
24482 
24490         public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') {
24491                 $this->htmlLinkColorArray = $color;
24492                 $this->htmlLinkFontStyle = $fontstyle;
24493         }
24494 
24505         public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) {
24506                 $supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt');
24507                 $retval = 0;
24508                 $value = 0;
24509                 $unit = 'px';
24510                 $k = $this->k;
24511                 if ($points) {
24512                         $k = 1;
24513                 }
24514                 if (in_array($defaultunit, $supportedunits)) {
24515                         $unit = $defaultunit;
24516                 }
24517                 if (is_numeric($htmlval)) {
24518                         $value = floatval($htmlval);
24519                 } elseif (preg_match('/([0-9\.\-\+]+)/', $htmlval, $mnum)) {
24520                         $value = floatval($mnum[1]);
24521                         if (preg_match('/([a-z%]+)/', $htmlval, $munit)) {
24522                                 if (in_array($munit[1], $supportedunits)) {
24523                                         $unit = $munit[1];
24524                                 }
24525                         }
24526                 }
24527                 switch ($unit) {
24528                         // percentage
24529                         case '%': {
24530                                 $retval = (($value * $refsize) / 100);
24531                                 break;
24532                         }
24533                         // relative-size
24534                         case 'em': {
24535                                 $retval = ($value * $refsize);
24536                                 break;
24537                         }
24538                         // height of lower case 'x' (about half the font-size)
24539                         case 'ex': {
24540                                 $retval = $value * ($refsize / 2);
24541                                 break;
24542                         }
24543                         // absolute-size
24544                         case 'in': {
24545                                 $retval = ($value * $this->dpi) / $k;
24546                                 break;
24547                         }
24548                         // centimeters
24549                         case 'cm': {
24550                                 $retval = ($value / 2.54 * $this->dpi) / $k;
24551                                 break;
24552                         }
24553                         // millimeters
24554                         case 'mm': {
24555                                 $retval = ($value / 25.4 * $this->dpi) / $k;
24556                                 break;
24557                         }
24558                         // one pica is 12 points
24559                         case 'pc': {
24560                                 $retval = ($value * 12) / $k;
24561                                 break;
24562                         }
24563                         // points
24564                         case 'pt': {
24565                                 $retval = $value / $k;
24566                                 break;
24567                         }
24568                         // pixels
24569                         case 'px': {
24570                                 $retval = $this->pixelsToUnits($value);
24571                                 break;
24572                         }
24573                 }
24574                 return $retval;
24575         }
24576 
24584         public function intToRoman($number) {
24585                 $roman = '';
24586                 while ($number >= 1000) {
24587                         $roman .= 'M';
24588                         $number -= 1000;
24589                 }
24590                 while ($number >= 900) {
24591                         $roman .= 'CM';
24592                         $number -= 900;
24593                 }
24594                 while ($number >= 500) {
24595                         $roman .= 'D';
24596                         $number -= 500;
24597                 }
24598                 while ($number >= 400) {
24599                         $roman .= 'CD';
24600                         $number -= 400;
24601                 }
24602                 while ($number >= 100) {
24603                         $roman .= 'C';
24604                         $number -= 100;
24605                 }
24606                 while ($number >= 90) {
24607                         $roman .= 'XC';
24608                         $number -= 90;
24609                 }
24610                 while ($number >= 50) {
24611                         $roman .= 'L';
24612                         $number -= 50;
24613                 }
24614                 while ($number >= 40) {
24615                         $roman .= 'XL';
24616                         $number -= 40;
24617                 }
24618                 while ($number >= 10) {
24619                         $roman .= 'X';
24620                         $number -= 10;
24621                 }
24622                 while ($number >= 9) {
24623                         $roman .= 'IX';
24624                         $number -= 9;
24625                 }
24626                 while ($number >= 5) {
24627                         $roman .= 'V';
24628                         $number -= 5;
24629                 }
24630                 while ($number >= 4) {
24631                         $roman .= 'IV';
24632                         $number -= 4;
24633                 }
24634                 while ($number >= 1) {
24635                         $roman .= 'I';
24636                         --$number;
24637                 }
24638                 return $roman;
24639         }
24640 
24649         protected function putHtmlListBullet($listdepth, $listtype='', $size=10) {
24650                 $size /= $this->k;
24651                 $fill = '';
24652                 $bgcolor = $this->bgcolor;
24653                 $color = $this->fgcolor;
24654                 $strokecolor = $this->strokecolor;
24655                 $width = 0;
24656                 $textitem = '';
24657                 $tmpx = $this->x;
24658                 $lspace = $this->GetStringWidth('  ');
24659                 if ($listtype == '^') {
24660                         // special symbol used for avoid justification of rect bullet
24661                         $this->lispacer = '';
24662                         return;
24663                 } elseif ($listtype == '!') {
24664                         // set default list type for unordered list
24665                         $deftypes = array('disc', 'circle', 'square');
24666                         $listtype = $deftypes[($listdepth - 1) % 3];
24667                 } elseif ($listtype == '#') {
24668                         // set default list type for ordered list
24669                         $listtype = 'decimal';
24670                 } elseif (substr($listtype, 0, 4) == 'img|') {
24671                         // custom image type ('img|type|width|height|image.ext')
24672                         $img = explode('|', $listtype);
24673                         $listtype = 'img';
24674                 }
24675                 switch ($listtype) {
24676                         // unordered types
24677                         case 'none': {
24678                                 break;
24679                         }
24680                         case 'disc': {
24681                                 $r = $size / 6;
24682                                 $lspace += (2 * $r);
24683                                 if ($this->rtl) {
24684                                         $this->x += $lspace;
24685                                 } else {
24686                                         $this->x -= $lspace;
24687                                 }
24688                                 $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), $r, 0, 360, 'F', array(), $color, 8);
24689                                 break;
24690                         }
24691                         case 'circle': {
24692                                 $r = $size / 6;
24693                                 $lspace += (2 * $r);
24694                                 if ($this->rtl) {
24695                                         $this->x += $lspace;
24696                                 } else {
24697                                         $this->x -= $lspace;
24698                                 }
24699                                 $prev_line_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor;
24700                                 $new_line_style = array('width' => ($r / 3), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'phase' => 0, 'color'=>$color);
24701                                 $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), ($r * (1 - (1/6))), 0, 360, 'D', $new_line_style, array(), 8);
24702                                 $this->_out($prev_line_style); // restore line settings
24703                                 break;
24704                         }
24705                         case 'square': {
24706                                 $l = $size / 3;
24707                                 $lspace += $l;
24708                                 if ($this->rtl) {;
24709                                         $this->x += $lspace;
24710                                 } else {
24711                                         $this->x -= $lspace;
24712                                 }
24713                                 $this->Rect($this->x, ($this->y + (($this->lasth - $l) / 2)), $l, $l, 'F', array(), $color);
24714                                 break;
24715                         }
24716                         case 'img': {
24717                                 // 1=>type, 2=>width, 3=>height, 4=>image.ext
24718                                 $lspace += $img[2];
24719                                 if ($this->rtl) {;
24720                                         $this->x += $lspace;
24721                                 } else {
24722                                         $this->x -= $lspace;
24723                                 }
24724                                 $imgtype = strtolower($img[1]);
24725                                 $prev_y = $this->y;
24726                                 switch ($imgtype) {
24727                                         case 'svg': {
24728                                                 $this->ImageSVG($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', 'T', '', 0, false);
24729                                                 break;
24730                                         }
24731                                         case 'ai':
24732                                         case 'eps': {
24733                                                 $this->ImageEps($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', true, 'T', '', 0, false);
24734                                                 break;
24735                                         }
24736                                         default: {
24737                                                 $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);
24738                                                 break;
24739                                         }
24740                                 }
24741                                 $this->y = $prev_y;
24742                                 break;
24743                         }
24744                         // ordered types
24745                         // $this->listcount[$this->listnum];
24746                         // $textitem
24747                         case '1':
24748                         case 'decimal': {
24749                                 $textitem = $this->listcount[$this->listnum];
24750                                 break;
24751                         }
24752                         case 'decimal-leading-zero': {
24753                                 $textitem = sprintf('%02d', $this->listcount[$this->listnum]);
24754                                 break;
24755                         }
24756                         case 'i':
24757                         case 'lower-roman': {
24758                                 $textitem = strtolower($this->intToRoman($this->listcount[$this->listnum]));
24759                                 break;
24760                         }
24761                         case 'I':
24762                         case 'upper-roman': {
24763                                 $textitem = $this->intToRoman($this->listcount[$this->listnum]);
24764                                 break;
24765                         }
24766                         case 'a':
24767                         case 'lower-alpha':
24768                         case 'lower-latin': {
24769                                 $textitem = chr(97 + $this->listcount[$this->listnum] - 1);
24770                                 break;
24771                         }
24772                         case 'A':
24773                         case 'upper-alpha':
24774                         case 'upper-latin': {
24775                                 $textitem = chr(65 + $this->listcount[$this->listnum] - 1);
24776                                 break;
24777                         }
24778                         case 'lower-greek': {
24779                                 $textitem = $this->unichr(945 + $this->listcount[$this->listnum] - 1);
24780                                 break;
24781                         }
24782                         /*
24783                         // Types to be implemented (special handling)
24784                         case 'hebrew': {
24785                                 break;
24786                         }
24787                         case 'armenian': {
24788                                 break;
24789                         }
24790                         case 'georgian': {
24791                                 break;
24792                         }
24793                         case 'cjk-ideographic': {
24794                                 break;
24795                         }
24796                         case 'hiragana': {
24797                                 break;
24798                         }
24799                         case 'katakana': {
24800                                 break;
24801                         }
24802                         case 'hiragana-iroha': {
24803                                 break;
24804                         }
24805                         case 'katakana-iroha': {
24806                                 break;
24807                         }
24808                         */
24809                         default: {
24810                                 $textitem = $this->listcount[$this->listnum];
24811                         }
24812                 }
24813                 if (!$this->empty_string($textitem)) {
24814                         // Check whether we need a new page or new column
24815                         $prev_y = $this->y;
24816                         $h = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
24817                         if ($this->checkPageBreak($h) OR ($this->y < $prev_y)) {
24818                                 $tmpx = $this->x;
24819                         }
24820                         // print ordered item
24821                         if ($this->rtl) {
24822                                 $textitem = '.'.$textitem;
24823                         } else {
24824                                 $textitem = $textitem.'.';
24825                         }
24826                         $lspace += $this->GetStringWidth($textitem);
24827                         if ($this->rtl) {
24828                                 $this->x += $lspace;
24829                         } else {
24830                                 $this->x -= $lspace;
24831                         }
24832                         $this->Write($this->lasth, $textitem, '', false, '', false, 0, false);
24833                 }
24834                 $this->x = $tmpx;
24835                 $this->lispacer = '^';
24836                 // restore colors
24837                 $this->SetFillColorArray($bgcolor);
24838                 $this->SetDrawColorArray($strokecolor);
24839                 $this->SettextColorArray($color);
24840         }
24841 
24848         protected function getGraphicVars() {
24849                 $grapvars = array(
24850                         'FontFamily' => $this->FontFamily,
24851                         'FontStyle' => $this->FontStyle,
24852                         'FontSizePt' => $this->FontSizePt,
24853                         'rMargin' => $this->rMargin,
24854                         'lMargin' => $this->lMargin,
24855                         'cell_padding' => $this->cell_padding,
24856                         'cell_margin' => $this->cell_margin,
24857                         'LineWidth' => $this->LineWidth,
24858                         'linestyleWidth' => $this->linestyleWidth,
24859                         'linestyleCap' => $this->linestyleCap,
24860                         'linestyleJoin' => $this->linestyleJoin,
24861                         'linestyleDash' => $this->linestyleDash,
24862                         'textrendermode' => $this->textrendermode,
24863                         'textstrokewidth' => $this->textstrokewidth,
24864                         'DrawColor' => $this->DrawColor,
24865                         'FillColor' => $this->FillColor,
24866                         'TextColor' => $this->TextColor,
24867                         'ColorFlag' => $this->ColorFlag,
24868                         'bgcolor' => $this->bgcolor,
24869                         'fgcolor' => $this->fgcolor,
24870                         'htmlvspace' => $this->htmlvspace,
24871                         'listindent' => $this->listindent,
24872                         'listindentlevel' => $this->listindentlevel,
24873                         'listnum' => $this->listnum,
24874                         'listordered' => $this->listordered,
24875                         'listcount' => $this->listcount,
24876                         'lispacer' => $this->lispacer,
24877                         'cell_height_ratio' => $this->cell_height_ratio,
24878                         'font_stretching' => $this->font_stretching,
24879                         'font_spacing' => $this->font_spacing,
24880                         // extended
24881                         'lasth' => $this->lasth,
24882                         'tMargin' => $this->tMargin,
24883                         'bMargin' => $this->bMargin,
24884                         'AutoPageBreak' => $this->AutoPageBreak,
24885                         'PageBreakTrigger' => $this->PageBreakTrigger,
24886                         'x' => $this->x,
24887                         'y' => $this->y,
24888                         'w' => $this->w,
24889                         'h' => $this->h,
24890                         'wPt' => $this->wPt,
24891                         'hPt' => $this->hPt,
24892                         'fwPt' => $this->fwPt,
24893                         'fhPt' => $this->fhPt,
24894                         'page' => $this->page,
24895                         'current_column' => $this->current_column,
24896                         'num_columns' => $this->num_columns
24897                         );
24898                 return $grapvars;
24899         }
24900 
24908         protected function setGraphicVars($gvars, $extended=false) {
24909                 $this->FontFamily = $gvars['FontFamily'];
24910                 $this->FontStyle = $gvars['FontStyle'];
24911                 $this->FontSizePt = $gvars['FontSizePt'];
24912                 $this->rMargin = $gvars['rMargin'];
24913                 $this->lMargin = $gvars['lMargin'];
24914                 $this->cell_padding = $gvars['cell_padding'];
24915                 $this->cell_margin = $gvars['cell_margin'];
24916                 $this->LineWidth = $gvars['LineWidth'];
24917                 $this->linestyleWidth = $gvars['linestyleWidth'];
24918                 $this->linestyleCap = $gvars['linestyleCap'];
24919                 $this->linestyleJoin = $gvars['linestyleJoin'];
24920                 $this->linestyleDash = $gvars['linestyleDash'];
24921                 $this->textrendermode = $gvars['textrendermode'];
24922                 $this->textstrokewidth = $gvars['textstrokewidth'];
24923                 $this->DrawColor = $gvars['DrawColor'];
24924                 $this->FillColor = $gvars['FillColor'];
24925                 $this->TextColor = $gvars['TextColor'];
24926                 $this->ColorFlag = $gvars['ColorFlag'];
24927                 $this->bgcolor = $gvars['bgcolor'];
24928                 $this->fgcolor = $gvars['fgcolor'];
24929                 $this->htmlvspace = $gvars['htmlvspace'];
24930                 $this->listindent = $gvars['listindent'];
24931                 $this->listindentlevel = $gvars['listindentlevel'];
24932                 $this->listnum = $gvars['listnum'];
24933                 $this->listordered = $gvars['listordered'];
24934                 $this->listcount = $gvars['listcount'];
24935                 $this->lispacer = $gvars['lispacer'];
24936                 $this->cell_height_ratio = $gvars['cell_height_ratio'];
24937                 $this->font_stretching = $gvars['font_stretching'];
24938                 $this->font_spacing = $gvars['font_spacing'];
24939                 if ($extended) {
24940                         // restore extended values
24941                         $this->lasth = $gvars['lasth'];
24942                         $this->tMargin = $gvars['tMargin'];
24943                         $this->bMargin = $gvars['bMargin'];
24944                         $this->AutoPageBreak = $gvars['AutoPageBreak'];
24945                         $this->PageBreakTrigger = $gvars['PageBreakTrigger'];
24946                         $this->x = $gvars['x'];
24947                         $this->y = $gvars['y'];
24948                         $this->w = $gvars['w'];
24949                         $this->h = $gvars['h'];
24950                         $this->wPt = $gvars['wPt'];
24951                         $this->hPt = $gvars['hPt'];
24952                         $this->fwPt = $gvars['fwPt'];
24953                         $this->fhPt = $gvars['fhPt'];
24954                         $this->page = $gvars['page'];
24955                         $this->current_column = $gvars['current_column'];
24956                         $this->num_columns = $gvars['num_columns'];
24957                 }
24958                 $this->_out(''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor.'');
24959                 if (!$this->empty_string($this->FontFamily)) {
24960                         $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
24961                 }
24962         }
24963 
24971         protected function getObjFilename($name) {
24972                 return tempnam(K_PATH_CACHE, $name.'_');
24973         }
24974 
24983         protected function writeDiskCache($filename, $data, $append=false) {
24984                 if ($append) {
24985                         $fmode = 'ab+';
24986                 } else {
24987                         $fmode = 'wb+';
24988                 }
24989                 $f = @fopen($filename, $fmode);
24990                 if (!$f) {
24991                         $this->Error('Unable to write cache file: '.$filename);
24992                 } else {
24993                         fwrite($f, $data);
24994                         fclose($f);
24995                 }
24996                 // update file length (needed for transactions)
24997                 if (!isset($this->cache_file_length['_'.$filename])) {
24998                         $this->cache_file_length['_'.$filename] = strlen($data);
24999                 } else {
25000                         $this->cache_file_length['_'.$filename] += strlen($data);
25001                 }
25002         }
25003 
25011         protected function readDiskCache($filename) {
25012                 return file_get_contents($filename);
25013         }
25014 
25021         protected function setBuffer($data) {
25022                 $this->bufferlen += strlen($data);
25023                 if ($this->diskcache) {
25024                         if (!isset($this->buffer) OR $this->empty_string($this->buffer)) {
25025                                 $this->buffer = $this->getObjFilename('buffer');
25026                         }
25027                         $this->writeDiskCache($this->buffer, $data, true);
25028                 } else {
25029                         $this->buffer .= $data;
25030                 }
25031         }
25032 
25039         protected function replaceBuffer($data) {
25040                 $this->bufferlen = strlen($data);
25041                 if ($this->diskcache) {
25042                         if (!isset($this->buffer) OR $this->empty_string($this->buffer)) {
25043                                 $this->buffer = $this->getObjFilename('buffer');
25044                         }
25045                         $this->writeDiskCache($this->buffer, $data, false);
25046                 } else {
25047                         $this->buffer = $data;
25048                 }
25049         }
25050 
25057         protected function getBuffer() {
25058                 if ($this->diskcache) {
25059                         return $this->readDiskCache($this->buffer);
25060                 } else {
25061                         return $this->buffer;
25062                 }
25063         }
25064 
25073         protected function setPageBuffer($page, $data, $append=false) {
25074                 if ($this->diskcache) {
25075                         if (!isset($this->pages[$page])) {
25076                                 $this->pages[$page] = $this->getObjFilename('page'.$page);
25077                         }
25078                         $this->writeDiskCache($this->pages[$page], $data, $append);
25079                 } else {
25080                         if ($append) {
25081                                 $this->pages[$page] .= $data;
25082                         } else {
25083                                 $this->pages[$page] = $data;
25084                         }
25085                 }
25086                 if ($append AND isset($this->pagelen[$page])) {
25087                         $this->pagelen[$page] += strlen($data);
25088                 } else {
25089                         $this->pagelen[$page] = strlen($data);
25090                 }
25091         }
25092 
25100         protected function getPageBuffer($page) {
25101                 if ($this->diskcache) {
25102                         return $this->readDiskCache($this->pages[$page]);
25103                 } elseif (isset($this->pages[$page])) {
25104                         return $this->pages[$page];
25105                 }
25106                 return false;
25107         }
25108 
25116         protected function setImageBuffer($image, $data) {
25117                 if ($this->diskcache) {
25118                         if (!isset($this->images[$image])) {
25119                                 $this->images[$image] = $this->getObjFilename('image'.$image);
25120                         }
25121                         $this->writeDiskCache($this->images[$image], serialize($data));
25122                 } else {
25123                         $this->images[$image] = $data;
25124                 }
25125                 if (!in_array($image, $this->imagekeys)) {
25126                         $this->imagekeys[] = $image;
25127                         ++$this->numimages;
25128                 }
25129         }
25130 
25139         protected function setImageSubBuffer($image, $key, $data) {
25140                 if (!isset($this->images[$image])) {
25141                         $this->setImageBuffer($image, array());
25142                 }
25143                 if ($this->diskcache) {
25144                         $tmpimg = $this->getImageBuffer($image);
25145                         $tmpimg[$key] = $data;
25146                         $this->writeDiskCache($this->images[$image], serialize($tmpimg));
25147                 } else {
25148                         $this->images[$image][$key] = $data;
25149                 }
25150         }
25151 
25159         protected function getImageBuffer($image) {
25160                 if ($this->diskcache AND isset($this->images[$image])) {
25161                         return unserialize($this->readDiskCache($this->images[$image]));
25162                 } elseif (isset($this->images[$image])) {
25163                         return $this->images[$image];
25164                 }
25165                 return false;
25166         }
25167 
25175         protected function setFontBuffer($font, $data) {
25176                 if ($this->diskcache) {
25177                         if (!isset($this->fonts[$font])) {
25178                                 $this->fonts[$font] = $this->getObjFilename('font');
25179                         }
25180                         $this->writeDiskCache($this->fonts[$font], serialize($data));
25181                 } else {
25182                         $this->fonts[$font] = $data;
25183                 }
25184                 if (!in_array($font, $this->fontkeys)) {
25185                         $this->fontkeys[] = $font;
25186                         // store object ID for current font
25187                         ++$this->n;
25188                         $this->font_obj_ids[$font] = $this->n;
25189                         $this->setFontSubBuffer($font, 'n', $this->n);
25190                 }
25191         }
25192 
25201         protected function setFontSubBuffer($font, $key, $data) {
25202                 if (!isset($this->fonts[$font])) {
25203                         $this->setFontBuffer($font, array());
25204                 }
25205                 if ($this->diskcache) {
25206                         $tmpfont = $this->getFontBuffer($font);
25207                         $tmpfont[$key] = $data;
25208                         $this->writeDiskCache($this->fonts[$font], serialize($tmpfont));
25209                 } else {
25210                         $this->fonts[$font][$key] = $data;
25211                 }
25212         }
25213 
25221         protected function getFontBuffer($font) {
25222                 if ($this->diskcache AND isset($this->fonts[$font])) {
25223                         return unserialize($this->readDiskCache($this->fonts[$font]));
25224                 } elseif (isset($this->fonts[$font])) {
25225                         return $this->fonts[$font];
25226                 }
25227                 return false;
25228         }
25229 
25238         public function movePage($frompage, $topage) {
25239                 if (($frompage > $this->numpages) OR ($frompage <= $topage)) {
25240                         return false;
25241                 }
25242                 if ($frompage == $this->page) {
25243                         // close the page before moving it
25244                         $this->endPage();
25245                 }
25246                 // move all page-related states
25247                 $tmppage = $this->getPageBuffer($frompage);
25248                 $tmppagedim = $this->pagedim[$frompage];
25249                 $tmppagelen = $this->pagelen[$frompage];
25250                 $tmpintmrk = $this->intmrk[$frompage];
25251                 $tmpbordermrk = $this->bordermrk[$frompage];
25252                 $tmpcntmrk = $this->cntmrk[$frompage];
25253                 if (isset($this->footerpos[$frompage])) {
25254                         $tmpfooterpos = $this->footerpos[$frompage];
25255                 }
25256                 if (isset($this->footerlen[$frompage])) {
25257                         $tmpfooterlen = $this->footerlen[$frompage];
25258                 }
25259                 if (isset($this->transfmrk[$frompage])) {
25260                         $tmptransfmrk = $this->transfmrk[$frompage];
25261                 }
25262                 if (isset($this->PageAnnots[$frompage])) {
25263                         $tmpannots = $this->PageAnnots[$frompage];
25264                 }
25265                 if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) {
25266                         for ($i = $frompage; $i > $topage; --$i) {
25267                                 if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $frompage)) {
25268                                         --$this->pagegroups[$this->newpagegroup[$i]];
25269                                         break;
25270                                 }
25271                         }
25272                         for ($i = $topage; $i > 0; --$i) {
25273                                 if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $topage)) {
25274                                         ++$this->pagegroups[$this->newpagegroup[$i]];
25275                                         break;
25276                                 }
25277                         }
25278                 }
25279                 for ($i = $frompage; $i > $topage; --$i) {
25280                         $j = $i - 1;
25281                         // shift pages down
25282                         $this->setPageBuffer($i, $this->getPageBuffer($j));
25283                         $this->pagedim[$i] = $this->pagedim[$j];
25284                         $this->pagelen[$i] = $this->pagelen[$j];
25285                         $this->intmrk[$i] = $this->intmrk[$j];
25286                         $this->bordermrk[$i] = $this->bordermrk[$j];
25287                         $this->cntmrk[$i] = $this->cntmrk[$j];
25288                         if (isset($this->footerpos[$j])) {
25289                                 $this->footerpos[$i] = $this->footerpos[$j];
25290                         } elseif (isset($this->footerpos[$i])) {
25291                                 unset($this->footerpos[$i]);
25292                         }
25293                         if (isset($this->footerlen[$j])) {
25294                                 $this->footerlen[$i] = $this->footerlen[$j];
25295                         } elseif (isset($this->footerlen[$i])) {
25296                                 unset($this->footerlen[$i]);
25297                         }
25298                         if (isset($this->transfmrk[$j])) {
25299                                 $this->transfmrk[$i] = $this->transfmrk[$j];
25300                         } elseif (isset($this->transfmrk[$i])) {
25301                                 unset($this->transfmrk[$i]);
25302                         }
25303                         if (isset($this->PageAnnots[$j])) {
25304                                 $this->PageAnnots[$i] = $this->PageAnnots[$j];
25305                         } elseif (isset($this->PageAnnots[$i])) {
25306                                 unset($this->PageAnnots[$i]);
25307                         }
25308                         if (isset($this->newpagegroup[$j])) {
25309                                 $this->newpagegroup[$i] = $this->newpagegroup[$j];
25310                                 unset($this->newpagegroup[$j]);
25311                         }
25312                         if ($this->currpagegroup == $j) {
25313                                 $this->currpagegroup = $i;
25314                         }
25315                 }
25316                 $this->setPageBuffer($topage, $tmppage);
25317                 $this->pagedim[$topage] = $tmppagedim;
25318                 $this->pagelen[$topage] = $tmppagelen;
25319                 $this->intmrk[$topage] = $tmpintmrk;
25320                 $this->bordermrk[$topage] = $tmpbordermrk;
25321                 $this->cntmrk[$topage] = $tmpcntmrk;
25322                 if (isset($tmpfooterpos)) {
25323                         $this->footerpos[$topage] = $tmpfooterpos;
25324                 } elseif (isset($this->footerpos[$topage])) {
25325                         unset($this->footerpos[$topage]);
25326                 }
25327                 if (isset($tmpfooterlen)) {
25328                         $this->footerlen[$topage] = $tmpfooterlen;
25329                 } elseif (isset($this->footerlen[$topage])) {
25330                         unset($this->footerlen[$topage]);
25331                 }
25332                 if (isset($tmptransfmrk)) {
25333                         $this->transfmrk[$topage] = $tmptransfmrk;
25334                 } elseif (isset($this->transfmrk[$topage])) {
25335                         unset($this->transfmrk[$topage]);
25336                 }
25337                 if (isset($tmpannots)) {
25338                         $this->PageAnnots[$topage] = $tmpannots;
25339                 } elseif (isset($this->PageAnnots[$topage])) {
25340                         unset($this->PageAnnots[$topage]);
25341                 }
25342                 // adjust outlines
25343                 $tmpoutlines = $this->outlines;
25344                 foreach ($tmpoutlines as $key => $outline) {
25345                         if (($outline['p'] >= $topage) AND ($outline['p'] < $frompage)) {
25346                                 $this->outlines[$key]['p'] = ($outline['p'] + 1);
25347                         } elseif ($outline['p'] == $frompage) {
25348                                 $this->outlines[$key]['p'] = $topage;
25349                         }
25350                 }
25351                 // adjust dests
25352                 $tmpdests = $this->dests;
25353                 foreach ($tmpdests as $key => $dest) {
25354                         if (($dest['p'] >= $topage) AND ($dest['p'] < $frompage)) {
25355                                 $this->dests[$key]['p'] = ($dest['p'] + 1);
25356                         } elseif ($dest['p'] == $frompage) {
25357                                 $this->dests[$key]['p'] = $topage;
25358                         }
25359                 }
25360                 // adjust links
25361                 $tmplinks = $this->links;
25362                 foreach ($tmplinks as $key => $link) {
25363                         if (($link[0] >= $topage) AND ($link[0] < $frompage)) {
25364                                 $this->links[$key][0] = ($link[0] + 1);
25365                         } elseif ($link[0] == $frompage) {
25366                                 $this->links[$key][0] = $topage;
25367                         }
25368                 }
25369                 // adjust javascript
25370                 $tmpjavascript = $this->javascript;
25371                 global $jfrompage, $jtopage;
25372                 $jfrompage = $frompage;
25373                 $jtopage = $topage;
25374                 $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/',
25375                         create_function('$matches', 'global $jfrompage, $jtopage;
25376                         $pagenum = intval($matches[3]) + 1;
25377                         if (($pagenum >= $jtopage) AND ($pagenum < $jfrompage)) {
25378                                 $newpage = ($pagenum + 1);
25379                         } elseif ($pagenum == $jfrompage) {
25380                                 $newpage = $jtopage;
25381                         } else {
25382                                 $newpage = $pagenum;
25383                         }
25384                         --$newpage;
25385                         return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript);
25386                 // return to last page
25387                 $this->lastPage(true);
25388                 return true;
25389         }
25390 
25398         public function deletePage($page) {
25399                 if (($page < 1) OR ($page > $this->numpages)) {
25400                         return false;
25401                 }
25402                 // delete current page
25403                 unset($this->pages[$page]);
25404                 unset($this->pagedim[$page]);
25405                 unset($this->pagelen[$page]);
25406                 unset($this->intmrk[$page]);
25407                 unset($this->bordermrk[$page]);
25408                 unset($this->cntmrk[$page]);
25409                 if (isset($this->footerpos[$page])) {
25410                         unset($this->footerpos[$page]);
25411                 }
25412                 if (isset($this->footerlen[$page])) {
25413                         unset($this->footerlen[$page]);
25414                 }
25415                 if (isset($this->transfmrk[$page])) {
25416                         unset($this->transfmrk[$page]);
25417                 }
25418                 if (isset($this->PageAnnots[$page])) {
25419                         unset($this->PageAnnots[$page]);
25420                 }
25421                 if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) {
25422                         for ($i = $page; $i > 0; --$i) {
25423                                 if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $page)) {
25424                                         --$this->pagegroups[$this->newpagegroup[$i]];
25425                                         break;
25426                                 }
25427                         }
25428                 }
25429                 if (isset($this->pageopen[$page])) {
25430                         unset($this->pageopen[$page]);
25431                 }
25432                 if ($page < $this->numpages) {
25433                         // update remaining pages
25434                         for ($i = $page; $i < $this->numpages; ++$i) {
25435                                 $j = $i + 1;
25436                                 // shift pages
25437                                 $this->setPageBuffer($i, $this->getPageBuffer($j));
25438                                 $this->pagedim[$i] = $this->pagedim[$j];
25439                                 $this->pagelen[$i] = $this->pagelen[$j];
25440                                 $this->intmrk[$i] = $this->intmrk[$j];
25441                                 $this->bordermrk[$i] = $this->bordermrk[$j];
25442                                 $this->cntmrk[$i] = $this->cntmrk[$j];
25443                                 if (isset($this->footerpos[$j])) {
25444                                         $this->footerpos[$i] = $this->footerpos[$j];
25445                                 } elseif (isset($this->footerpos[$i])) {
25446                                         unset($this->footerpos[$i]);
25447                                 }
25448                                 if (isset($this->footerlen[$j])) {
25449                                         $this->footerlen[$i] = $this->footerlen[$j];
25450                                 } elseif (isset($this->footerlen[$i])) {
25451                                         unset($this->footerlen[$i]);
25452                                 }
25453                                 if (isset($this->transfmrk[$j])) {
25454                                         $this->transfmrk[$i] = $this->transfmrk[$j];
25455                                 } elseif (isset($this->transfmrk[$i])) {
25456                                         unset($this->transfmrk[$i]);
25457                                 }
25458                                 if (isset($this->PageAnnots[$j])) {
25459                                         $this->PageAnnots[$i] = $this->PageAnnots[$j];
25460                                 } elseif (isset($this->PageAnnots[$i])) {
25461                                         unset($this->PageAnnots[$i]);
25462                                 }
25463                                 if (isset($this->newpagegroup[$j])) {
25464                                         $this->newpagegroup[$i] = $this->newpagegroup[$j];
25465                                         unset($this->newpagegroup[$j]);
25466                                 }
25467                                 if ($this->currpagegroup == $j) {
25468                                         $this->currpagegroup = $i;
25469                                 }
25470                                 if (isset($this->pageopen[$j])) {
25471                                         $this->pageopen[$i] = $this->pageopen[$j];
25472                                 } elseif (isset($this->pageopen[$i])) {
25473                                         unset($this->pageopen[$i]);
25474                                 }
25475                         }
25476                         // remove last page
25477                         unset($this->pages[$this->numpages]);
25478                         unset($this->pagedim[$this->numpages]);
25479                         unset($this->pagelen[$this->numpages]);
25480                         unset($this->intmrk[$this->numpages]);
25481                         unset($this->bordermrk[$this->numpages]);
25482                         unset($this->cntmrk[$this->numpages]);
25483                         if (isset($this->footerpos[$this->numpages])) {
25484                                 unset($this->footerpos[$this->numpages]);
25485                         }
25486                         if (isset($this->footerlen[$this->numpages])) {
25487                                 unset($this->footerlen[$this->numpages]);
25488                         }
25489                         if (isset($this->transfmrk[$this->numpages])) {
25490                                 unset($this->transfmrk[$this->numpages]);
25491                         }
25492                         if (isset($this->PageAnnots[$this->numpages])) {
25493                                 unset($this->PageAnnots[$this->numpages]);
25494                         }
25495                         if (isset($this->newpagegroup[$this->numpages])) {
25496                                 unset($this->newpagegroup[$this->numpages]);
25497                         }
25498                         if ($this->currpagegroup == $this->numpages) {
25499                                 $this->currpagegroup = ($this->numpages - 1);
25500                         }
25501                         if (isset($this->pagegroups[$this->numpages])) {
25502                                 unset($this->pagegroups[$this->numpages]);
25503                         }
25504                         if (isset($this->pageopen[$this->numpages])) {
25505                                 unset($this->pageopen[$this->numpages]);
25506                         }
25507                 }
25508                 --$this->numpages;
25509                 $this->page = $this->numpages;
25510                 // adjust outlines
25511                 $tmpoutlines = $this->outlines;
25512                 foreach ($tmpoutlines as $key => $outline) {
25513                         if ($outline['p'] > $page) {
25514                                 $this->outlines[$key]['p'] = $outline['p'] - 1;
25515                         } elseif ($outline['p'] == $page) {
25516                                 unset($this->outlines[$key]);
25517                         }
25518                 }
25519                 // adjust dests
25520                 $tmpdests = $this->dests;
25521                 foreach ($tmpdests as $key => $dest) {
25522                         if ($dest['p'] > $page) {
25523                                 $this->dests[$key]['p'] = $dest['p'] - 1;
25524                         } elseif ($dest['p'] == $page) {
25525                                 unset($this->dests[$key]);
25526                         }
25527                 }
25528                 // adjust links
25529                 $tmplinks = $this->links;
25530                 foreach ($tmplinks as $key => $link) {
25531                         if ($link[0] > $page) {
25532                                 $this->links[$key][0] = $link[0] - 1;
25533                         } elseif ($link[0] == $page) {
25534                                 unset($this->links[$key]);
25535                         }
25536                 }
25537                 // adjust javascript
25538                 $tmpjavascript = $this->javascript;
25539                 global $jpage;
25540                 $jpage = $page;
25541                 $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/',
25542                         create_function('$matches', 'global $jpage;
25543                         $pagenum = intval($matches[3]) + 1;
25544                         if ($pagenum >= $jpage) {
25545                                 $newpage = ($pagenum - 1);
25546                         } elseif ($pagenum == $jpage) {
25547                                 $newpage = 1;
25548                         } else {
25549                                 $newpage = $pagenum;
25550                         }
25551                         --$newpage;
25552                         return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript);
25553                 // return to last page
25554                 $this->lastPage(true);
25555                 return true;
25556         }
25557 
25565         public function copyPage($page=0) {
25566                 if ($page == 0) {
25567                         // default value
25568                         $page = $this->page;
25569                 }
25570                 if (($page < 1) OR ($page > $this->numpages)) {
25571                         return false;
25572                 }
25573                 // close the last page
25574                 $this->endPage();
25575                 // copy all page-related states
25576                 ++$this->numpages;
25577                 $this->page = $this->numpages;
25578                 $this->setPageBuffer($this->page, $this->getPageBuffer($page));
25579                 $this->pagedim[$this->page] = $this->pagedim[$page];
25580                 $this->pagelen[$this->page] = $this->pagelen[$page];
25581                 $this->intmrk[$this->page] = $this->intmrk[$page];
25582                 $this->bordermrk[$this->page] = $this->bordermrk[$page];
25583                 $this->cntmrk[$this->page] = $this->cntmrk[$page];
25584                 $this->pageopen[$this->page] = false;
25585                 if (isset($this->footerpos[$page])) {
25586                         $this->footerpos[$this->page] = $this->footerpos[$page];
25587                 }
25588                 if (isset($this->footerlen[$page])) {
25589                         $this->footerlen[$this->page] = $this->footerlen[$page];
25590                 }
25591                 if (isset($this->transfmrk[$page])) {
25592                         $this->transfmrk[$this->page] = $this->transfmrk[$page];
25593                 }
25594                 if (isset($this->PageAnnots[$page])) {
25595                         $this->PageAnnots[$this->page] = $this->PageAnnots[$page];
25596                 }
25597                 if (isset($this->newpagegroup[$page])) {
25598                         // start a new group
25599                         $this->newpagegroup[$this->page] = sizeof($this->newpagegroup) + 1;
25600                         $this->currpagegroup = $this->newpagegroup[$this->page];
25601                         $this->pagegroups[$this->currpagegroup] = 1;
25602                 } elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) {
25603                         ++$this->pagegroups[$this->currpagegroup];
25604                 }
25605                 // copy outlines
25606                 $tmpoutlines = $this->outlines;
25607                 foreach ($tmpoutlines as $key => $outline) {
25608                         if ($outline['p'] == $page) {
25609                                 $this->outlines[] = array('t' => $outline['t'], 'l' => $outline['l'], 'y' => $outline['y'], 'p' => $this->page, 's' => $outline['s'], 'c' => $outline['c']);
25610                         }
25611                 }
25612                 // copy links
25613                 $tmplinks = $this->links;
25614                 foreach ($tmplinks as $key => $link) {
25615                         if ($link[0] == $page) {
25616                                 $this->links[] = array($this->page, $link[1]);
25617                         }
25618                 }
25619                 // return to last page
25620                 $this->lastPage(true);
25621                 return true;
25622         }
25623 
25641         public function addTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC', $style='', $color=array(0,0,0)) {
25642                 $fontsize = $this->FontSizePt;
25643                 $fontfamily = $this->FontFamily;
25644                 $fontstyle = $this->FontStyle;
25645                 $w = $this->w - $this->lMargin - $this->rMargin;
25646                 $spacer = $this->GetStringWidth(chr(32)) * 4;
25647                 $page_first = $this->getPage();
25648                 $lmargin = $this->lMargin;
25649                 $rmargin = $this->rMargin;
25650                 $x_start = $this->GetX();
25651                 $current_page = $this->page;
25652                 $current_column = $this->current_column;
25653                 if ($this->empty_string($numbersfont)) {
25654                         $numbersfont = $this->default_monospaced_font;
25655                 }
25656                 if ($this->empty_string($filler)) {
25657                         $filler = ' ';
25658                 }
25659                 if ($this->empty_string($page)) {
25660                         $gap = ' ';
25661                 } else {
25662                         $gap = '';
25663                         if ($page < 1) {
25664                                 $page = 1;
25665                         }
25666                 }
25667                 $this->SetFont($numbersfont, $fontstyle, $fontsize);
25668                 $numwidth = $this->GetStringWidth('00000');
25669                 $maxpage = 0; //used for pages on attached documents
25670                 foreach ($this->outlines as $key => $outline) {
25671                         // check for extra pages (used for attachments)
25672                         if (($this->page > $page_first) AND ($outline['p'] >= $this->numpages)) {
25673                                 $outline['p'] += ($this->page - $page_first);
25674                         }
25675                         if ($this->rtl) {
25676                                 $aligntext = 'R';
25677                                 $alignnum = 'L';
25678                         } else {
25679                                 $aligntext = 'L';
25680                                 $alignnum = 'R';
25681                         }
25682                         if ($outline['l'] == 0) {
25683                                 $this->SetFont($fontfamily, $fontstyle.'B', $fontsize);
25684                         } else {
25685                                 $this->SetFont($fontfamily, $fontstyle, $fontsize - $outline['l']);
25686                         }
25687                         // check for page break
25688                         $this->checkPageBreak((2 * $this->FontSize * $this->cell_height_ratio));
25689                         // set margins and X position
25690                         if (($this->page == $current_page) AND ($this->current_column == $current_column)) {
25691                                 $this->lMargin = $lmargin;
25692                                 $this->rMargin = $rmargin;
25693                         } else {
25694                                 if ($this->current_column != $current_column) {
25695                                         if ($this->rtl) {
25696                                                 $x_start = $this->w - $this->columns[$this->current_column]['x'];
25697                                         } else {
25698                                                 $x_start = $this->columns[$this->current_column]['x'];
25699                                         }
25700                                 }
25701                                 $lmargin = $this->lMargin;
25702                                 $rmargin = $this->rMargin;
25703                                 $current_page = $this->page;
25704                                 $current_column = $this->current_column;
25705                         }
25706                         $this->SetX($x_start);
25707                         $indent = ($spacer * $outline['l']);
25708                         if ($this->rtl) {
25709                                 $this->x -= $indent;
25710                                 $this->rMargin = $this->w - $this->x;
25711                         } else {
25712                                 $this->x += $indent;
25713                                 $this->lMargin = $this->x;
25714                         }
25715                         $link = $this->AddLink();
25716                         $this->SetLink($link, $outline['y'], $outline['p']);
25717                         // write the text
25718                         if ($this->rtl) {
25719                                 $txt = ' '.$outline['t'];
25720                         } else {
25721                                 $txt = $outline['t'].' ';
25722                         }
25723                         $this->Write(0, $txt, $link, false, $aligntext, false, 0, false, false, 0, $numwidth, '');
25724                         if ($this->rtl) {
25725                                 $tw = $this->x - $this->lMargin;
25726                         } else {
25727                                 $tw = $this->w - $this->rMargin - $this->x;
25728                         }
25729                         $this->SetFont($numbersfont, $fontstyle, $fontsize);
25730                         if ($this->empty_string($page)) {
25731                                 $pagenum = $outline['p'];
25732                         } else {
25733                                 // placemark to be replaced with the correct number
25734                                 $pagenum = '{#'.($outline['p']).'}';
25735                                 if ($this->isUnicodeFont()) {
25736                                         $pagenum = '{'.$pagenum.'}';
25737                                 }
25738                                 $maxpage = max($maxpage, $outline['p']);
25739                         }
25740                         $fw = ($tw - $this->GetStringWidth($pagenum.$filler));
25741                         $numfills = floor($fw / $this->GetStringWidth($filler));
25742                         if ($numfills > 0) {
25743                                 $rowfill = str_repeat($filler, $numfills);
25744                         } else {
25745                                 $rowfill = '';
25746                         }
25747                         if ($this->rtl) {
25748                                 $pagenum = $pagenum.$gap.$rowfill;
25749                         } else {
25750                                 $pagenum = $rowfill.$gap.$pagenum;
25751                         }
25752                         // write the number
25753                         $this->Cell($tw, 0, $pagenum, 0, 1, $alignnum, 0, $link, 0);
25754                 }
25755                 $page_last = $this->getPage();
25756                 $maxpage = max($maxpage, $page_last);
25757                 $numpages = $page_last - $page_first + 1;
25758                 if (!$this->empty_string($page)) {
25759                         for ($p = $page_first; $p <= $page_last; ++$p) {
25760                                 // get page data
25761                                 $temppage = $this->getPageBuffer($p);
25762                                 for ($n = 1; $n <= $maxpage; ++$n) {
25763                                         // update page numbers
25764                                         $a = '{#'.$n.'}';
25765                                         // get page number aliases
25766                                         $pnalias = $this->getInternalPageNumberAliases($a);
25767                                         // calculate replacement number
25768                                         if (($n >= $page) AND ($n <= $this->numpages)) {
25769                                                 $np = $n + $numpages;
25770                                         } else {
25771                                                 $np = $n;
25772                                         }
25773                                         $na = $this->formatTOCPageNumber(($this->starting_page_number + $np - 1));
25774                                         $nu = $this->UTF8ToUTF16BE($na, false);
25775                                         // replace aliases with numbers
25776                                         foreach ($pnalias['u'] as $u) {
25777                                                 $sfill = str_repeat($filler, max(0, (strlen($u) - strlen($nu.' '))));
25778                                                 if ($this->rtl) {
25779                                                         $nr = $nu.$this->UTF8ToUTF16BE(' '.$sfill);
25780                                                 } else {
25781                                                         $nr = $this->UTF8ToUTF16BE($sfill.' ').$nu;
25782                                                 }
25783                                                 $temppage = str_replace($u, $nr, $temppage);
25784                                         }
25785                                         foreach ($pnalias['a'] as $a) {
25786                                                 $sfill = str_repeat($filler, max(0, (strlen($a) - strlen($na.' '))));
25787                                                 if ($this->rtl) {
25788                                                         $nr = $na.' '.$sfill;
25789                                                 } else {
25790                                                         $nr = $sfill.' '.$na;
25791                                                 }
25792                                                 $temppage = str_replace($a, $nr, $temppage);
25793                                         }
25794                                 }
25795                                 // save changes
25796                                 $this->setPageBuffer($p, $temppage);
25797                         }
25798                         // move pages
25799                         $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color);
25800                         for ($i = 0; $i < $numpages; ++$i) {
25801                                 $this->movePage($page_last, $page);
25802                         }
25803                 }
25804         }
25805 
25822         public function addHTMLTOC($page='', $toc_name='TOC', $templates=array(), $correct_align=true, $style='', $color=array(0,0,0)) {
25823                 $filler = ' ';
25824                 $prev_htmlLinkColorArray = $this->htmlLinkColorArray;
25825                 $prev_htmlLinkFontStyle = $this->htmlLinkFontStyle;
25826                 // set new style for link
25827                 $this->htmlLinkColorArray = array();
25828                 $this->htmlLinkFontStyle = '';
25829                 $page_first = $this->getPage();
25830                 // get the font type used for numbers in each template
25831                 $current_font = $this->FontFamily;
25832                 foreach ($templates as $level => $html) {
25833                         $dom = $this->getHtmlDomArray($html);
25834                         foreach ($dom as $key => $value) {
25835                                 if ($value['value'] == '#TOC_PAGE_NUMBER#') {
25836                                         $this->SetFont($dom[($key - 1)]['fontname']);
25837                                         $templates['F'.$level] = $this->isUnicodeFont();
25838                                 }
25839                         }
25840                 }
25841                 $this->SetFont($current_font);
25842                 $maxpage = 0; //used for pages on attached documents
25843                 foreach ($this->outlines as $key => $outline) {
25844                         // get HTML template
25845                         $row = $templates[$outline['l']];
25846                         if ($this->empty_string($page)) {
25847                                 $pagenum = $outline['p'];
25848                         } else {
25849                                 // placemark to be replaced with the correct number
25850                                 $pagenum = '{#'.($outline['p']).'}';
25851                                 if ($templates['F'.$outline['l']]) {
25852                                         $pagenum = '{'.$pagenum.'}';
25853                                 }
25854                                 $maxpage = max($maxpage, $outline['p']);
25855                         }
25856                         // replace templates with current values
25857                         $row = str_replace('#TOC_DESCRIPTION#', $outline['t'], $row);
25858                         $row = str_replace('#TOC_PAGE_NUMBER#', $pagenum, $row);
25859                         // add link to page
25860                         $row = '<a href="#'.$outline['p'].','.$outline['y'].'">'.$row.'</a>';
25861                         // write bookmark entry
25862                         $this->writeHTML($row, false, false, true, false, '');
25863                 }
25864                 // restore link styles
25865                 $this->htmlLinkColorArray = $prev_htmlLinkColorArray;
25866                 $this->htmlLinkFontStyle = $prev_htmlLinkFontStyle;
25867                 // move TOC page and replace numbers
25868                 $page_last = $this->getPage();
25869                 $maxpage = max($maxpage, $page_last);
25870                 $numpages = $page_last - $page_first + 1;
25871                 if (!$this->empty_string($page)) {
25872                         for ($p = $page_first; $p <= $page_last; ++$p) {
25873                                 // get page data
25874                                 $temppage = $this->getPageBuffer($p);
25875                                 for ($n = 1; $n <= $maxpage; ++$n) {
25876                                         // update page numbers
25877                                         $a = '{#'.$n.'}';
25878                                         // get page number aliases
25879                                         $pnalias = $this->getInternalPageNumberAliases($a);
25880                                         // calculate replacement number
25881                                         if ($n >= $page) {
25882                                                 $np = $n + $numpages;
25883                                         } else {
25884                                                 $np = $n;
25885                                         }
25886                                         $na = $this->formatTOCPageNumber(($this->starting_page_number + $np - 1));
25887                                         $nu = $this->UTF8ToUTF16BE($na, false);
25888                                         // replace aliases with numbers
25889                                         foreach ($pnalias['u'] as $u) {
25890                                                 if ($correct_align) {
25891                                                         $sfill = str_repeat($filler, (strlen($u) - strlen($nu.' ')));
25892                                                         if ($this->rtl) {
25893                                                                 $nr = $nu.$this->UTF8ToUTF16BE(' '.$sfill);
25894                                                         } else {
25895                                                                 $nr = $this->UTF8ToUTF16BE($sfill.' ').$nu;
25896                                                         }
25897                                                 } else {
25898                                                         $nr = $nu;
25899                                                 }
25900                                                 $temppage = str_replace($u, $nr, $temppage);
25901                                         }
25902                                         foreach ($pnalias['a'] as $a) {
25903                                                 if ($correct_align) {
25904                                                         $sfill = str_repeat($filler, (strlen($a) - strlen($na.' ')));
25905                                                         if ($this->rtl) {
25906                                                                 $nr = $na.' '.$sfill;
25907                                                         } else {
25908                                                                 $nr = $sfill.' '.$na;
25909                                                         }
25910                                                 } else {
25911                                                         $nr = $na;
25912                                                 }
25913                                                 $temppage = str_replace($a, $nr, $temppage);
25914                                         }
25915                                 }
25916                                 // save changes
25917                                 $this->setPageBuffer($p, $temppage);
25918                         }
25919                         // move pages
25920                         $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color);
25921                         for ($i = 0; $i < $numpages; ++$i) {
25922                                 $this->movePage($page_last, $page);
25923                         }
25924                 }
25925         }
25926 
25932         public function startTransaction() {
25933                 if (isset($this->objcopy)) {
25934                         // remove previous copy
25935                         $this->commitTransaction();
25936                 }
25937                 // record current page number and Y position
25938                 $this->start_transaction_page = $this->page;
25939                 $this->start_transaction_y = $this->y;
25940                 // clone current object
25941                 $this->objcopy = $this->objclone($this);
25942         }
25943 
25949         public function commitTransaction() {
25950                 if (isset($this->objcopy)) {
25951                         $this->objcopy->_destroy(true, true);
25952                         unset($this->objcopy);
25953                 }
25954         }
25955 
25963         public function rollbackTransaction($self=false) {
25964                 if (isset($this->objcopy)) {
25965                         if (isset($this->objcopy->diskcache) AND $this->objcopy->diskcache) {
25966                                 // truncate files to previous values
25967                                 foreach ($this->objcopy->cache_file_length as $file => $length) {
25968                                         $file = substr($file, 1);
25969                                         $handle = fopen($file, 'r+');
25970                                         ftruncate($handle, $length);
25971                                 }
25972                         }
25973                         $this->_destroy(true, true);
25974                         if ($self) {
25975                                 $objvars = get_object_vars($this->objcopy);
25976                                 foreach ($objvars as $key => $value) {
25977                                         $this->$key = $value;
25978                                 }
25979                         }
25980                         return $this->objcopy;
25981                 }
25982                 return $this;
25983         }
25984 
25992         public function objclone($object) {
25993                 return @clone($object);
25994         }
25995 
26003         public function empty_string($str) {
26004                 return (is_null($str) OR (is_string($str) AND (strlen($str) == 0)));
26005         }
26006 
26016         public function revstrpos($haystack, $needle, $offset = 0) {
26017                 $length = strlen($haystack);
26018                 $offset = ($offset > 0)?($length - $offset):abs($offset);
26019                 $pos = strpos(strrev($haystack), strrev($needle), $offset);
26020                 return ($pos === false)?false:($length - $pos - strlen($needle));
26021         }
26022 
26023         // --- MULTI COLUMNS METHODS -----------------------
26024 
26033         public function setEqualColumns($numcols=0, $width=0, $y='') {
26034                 $this->columns = array();
26035                 if ($numcols < 2) {
26036                         $numcols = 0;
26037                         $this->columns = array();
26038                 } else {
26039                         // maximum column width
26040                         $maxwidth = ($this->w - $this->original_lMargin - $this->original_rMargin) / $numcols;
26041                         if (($width == 0) OR ($width > $maxwidth)) {
26042                                 $width = $maxwidth;
26043                         }
26044                         if ($this->empty_string($y)) {
26045                                 $y = $this->y;
26046                         }
26047                         // space between columns
26048                         $space = (($this->w - $this->original_lMargin - $this->original_rMargin - ($numcols * $width)) / ($numcols - 1));
26049                         // fill the columns array (with, space, starting Y position)
26050                         for ($i = 0; $i < $numcols; ++$i) {
26051                                 $this->columns[$i] = array('w' => $width, 's' => $space, 'y' => $y);
26052                         }
26053                 }
26054                 $this->num_columns = $numcols;
26055                 $this->current_column = 0;
26056                 $this->column_start_page = $this->page;
26057                 $this->selectColumn(0);
26058         }
26059 
26065         public function resetColumns() {
26066                 $this->lMargin = $this->original_lMargin;
26067                 $this->rMargin = $this->original_rMargin;
26068                 $this->setEqualColumns();
26069         }
26070 
26078         public function setColumnsArray($columns) {
26079                 $this->columns = $columns;
26080                 $this->num_columns = count($columns);
26081                 $this->current_column = 0;
26082                 $this->column_start_page = $this->page;
26083                 $this->selectColumn(0);
26084         }
26085 
26092         public function selectColumn($col='') {
26093                 if (is_string($col)) {
26094                         $col = $this->current_column;
26095                 } elseif ($col >= $this->num_columns) {
26096                         $col = 0;
26097                 }
26098                 $xshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
26099                 $enable_thead = false;
26100                 if ($this->num_columns > 1) {
26101                         if ($col != $this->current_column) {
26102                                 // move Y pointer at the top of the column
26103                                 if ($this->column_start_page == $this->page) {
26104                                         $this->y = $this->columns[$col]['y'];
26105                                 } else {
26106                                         $this->y = $this->tMargin;
26107                                 }
26108                                 // Avoid to write table headers more than once
26109                                 if (($this->page > $this->maxselcol['page']) OR (($this->page == $this->maxselcol['page']) AND ($col > $this->maxselcol['column']))) {
26110                                         $enable_thead = true;
26111                                         $this->maxselcol['page'] = $this->page;
26112                                         $this->maxselcol['column'] = $col;
26113                                 }
26114                         }
26115                         $xshift = $this->colxshift;
26116                         // set X position of the current column by case
26117                         $listindent = ($this->listindentlevel * $this->listindent);
26118                         // calculate column X position
26119                         $colpos = 0;
26120                         for ($i = 0; $i < $col; ++$i) {
26121                                 $colpos += ($this->columns[$i]['w'] + $this->columns[$i]['s']);
26122                         }
26123                         if ($this->rtl) {
26124                                 $x = $this->w - $this->original_rMargin - $colpos;
26125                                 $this->rMargin = ($this->w - $x + $listindent);
26126                                 $this->lMargin = ($x - $this->columns[$col]['w']);
26127                                 $this->x = $x - $listindent;
26128                         } else {
26129                                 $x = $this->original_lMargin + $colpos;
26130                                 $this->lMargin = ($x + $listindent);
26131                                 $this->rMargin = ($this->w - $x - $this->columns[$col]['w']);
26132                                 $this->x = $x + $listindent;
26133                         }
26134                         $this->columns[$col]['x'] = $x;
26135                 }
26136                 $this->current_column = $col;
26137                 // fix for HTML mode
26138                 $this->newline = true;
26139                 // print HTML table header (if any)
26140                 if ((!$this->empty_string($this->thead)) AND (!$this->inthead)) {
26141                         if ($enable_thead) {
26142                                 // print table header
26143                                 $this->writeHTML($this->thead, false, false, false, false, '');
26144                                 $this->y += $xshift['s']['V'];
26145                                 // store end of header position
26146                                 if (!isset($this->columns[$col]['th'])) {
26147                                         $this->columns[$col]['th'] = array();
26148                                 }
26149                                 $this->columns[$col]['th']['\''.$this->page.'\''] = $this->y;
26150                                 $this->lasth = 0;
26151                         } elseif (isset($this->columns[$col]['th']['\''.$this->page.'\''])) {
26152                                 $this->y = $this->columns[$col]['th']['\''.$this->page.'\''];
26153                         }
26154                 }
26155                 // account for an html table cell over multiple columns
26156                 if ($this->rtl) {
26157                         $this->rMargin += $xshift['x'];
26158                         $this->x -= ($xshift['x'] + $xshift['p']['R']);
26159                 } else {
26160                         $this->lMargin += $xshift['x'];
26161                         $this->x += $xshift['x'] + $xshift['p']['L'];
26162                 }
26163         }
26164 
26171         public function getColumn() {
26172                 return $this->current_column;
26173         }
26174 
26181         public function getNumberOfColumns() {
26182                 return $this->num_columns;
26183         }
26184 
26192         public function serializeTCPDFtagParameters($pararray) {
26193                 return urlencode(serialize($pararray));
26194         }
26195 
26204         public function setTextRenderingMode($stroke=0, $fill=true, $clip=false) {
26205                 // Ref.: PDF 32000-1:2008 - 9.3.6 Text Rendering Mode
26206                 // convert text rendering parameters
26207                 if ($stroke < 0) {
26208                         $stroke = 0;
26209                 }
26210                 if ($fill === true) {
26211                         if ($stroke > 0) {
26212                                 if ($clip === true) {
26213                                         // Fill, then stroke text and add to path for clipping
26214                                         $textrendermode = 6;
26215                                 } else {
26216                                         // Fill, then stroke text
26217                                         $textrendermode = 2;
26218                                 }
26219                                 $textstrokewidth = $stroke;
26220                         } else {
26221                                 if ($clip === true) {
26222                                         // Fill text and add to path for clipping
26223                                         $textrendermode = 4;
26224                                 } else {
26225                                         // Fill text
26226                                         $textrendermode = 0;
26227                                 }
26228                         }
26229                 } else {
26230                         if ($stroke > 0) {
26231                                 if ($clip === true) {
26232                                         // Stroke text and add to path for clipping
26233                                         $textrendermode = 5;
26234                                 } else {
26235                                         // Stroke text
26236                                         $textrendermode = 1;
26237                                 }
26238                                 $textstrokewidth = $stroke;
26239                         } else {
26240                                 if ($clip === true) {
26241                                         // Add text to path for clipping
26242                                         $textrendermode = 7;
26243                                 } else {
26244                                         // Neither fill nor stroke text (invisible)
26245                                         $textrendermode = 3;
26246                                 }
26247                         }
26248                 }
26249                 $this->textrendermode = $textrendermode;
26250                 $this->textstrokewidth = $stroke * $this->k;
26251         }
26252 
26267         protected function hyphenateWord($word, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
26268                 $hyphenword = array(); // hyphens positions
26269                 $numchars = count($word);
26270                 if ($numchars <= $charmin) {
26271                         return $word;
26272                 }
26273                 $word_string = $this->UTF8ArrSubString($word);
26274                 // some words will be returned as-is
26275                 $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})(\]?)$/';
26276                 if (preg_match($pattern, $word_string) > 0) {
26277                         // email
26278                         return $word;
26279                 }
26280                 $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})(\]?)$/';
26281                 if (preg_match($pattern, $word_string) > 0) {
26282                         // URL
26283                         return $word;
26284                 }
26285                 if (isset($dictionary[$word_string])) {
26286                         return $this->UTF8StringToArray($dictionary[$word_string]);
26287                 }
26288                 // suround word with '_' characters
26289                 $tmpword = array_merge(array(95), $word, array(95));
26290                 $tmpnumchars = $numchars + 2;
26291                 $maxpos = $tmpnumchars - $charmin;
26292                 for ($pos = 0; $pos < $maxpos; ++$pos) {
26293                         $imax = min(($tmpnumchars - $pos), $charmax);
26294                         for ($i = $charmin; $i <= $imax; ++$i) {
26295                                 $subword = strtolower($this->UTF8ArrSubString($tmpword, $pos, $pos + $i));
26296                                 if (isset($patterns[$subword])) {
26297                                         $pattern = $this->UTF8StringToArray($patterns[$subword]);
26298                                         $pattern_length = count($pattern);
26299                                         $digits = 1;
26300                                         for ($j = 0; $j < $pattern_length; ++$j) {
26301                                                 // check if $pattern[$j] is a number
26302                                                 if (($pattern[$j] >= 48) AND ($pattern[$j] <= 57)) {
26303                                                         if ($j == 0) {
26304                                                                 $zero = $pos - 1;
26305                                                         } else {
26306                                                                 $zero = $pos + $j - $digits;
26307                                                         }
26308                                                         if (!isset($hyphenword[$zero]) OR ($hyphenword[$zero] != $pattern[$j])) {
26309                                                                 $hyphenword[$zero] = $this->unichr($pattern[$j]);
26310                                                         }
26311                                                         ++$digits;
26312                                                 }
26313                                         }
26314                                 }
26315                         }
26316                 }
26317                 $inserted = 0;
26318                 $maxpos = $numchars - $rightmin;
26319                 for ($i = $leftmin; $i <= $maxpos; ++$i) {
26320                         if (isset($hyphenword[$i]) AND (($hyphenword[$i] % 2) != 0)) {
26321                                 // 173 = soft hyphen character
26322                                 array_splice($word, $i + $inserted, 0, 173);
26323                                 ++$inserted;
26324                         }
26325                 }
26326                 return $word;
26327         }
26328 
26337         public function getHyphenPatternsFromTEX($file) {
26338                 // TEX patterns are available at:
26339                 // http://www.ctan.org/tex-archive/language/hyph-utf8/tex/generic/hyph-utf8/patterns/
26340                 $data = file_get_contents($file);
26341                 $patterns = array();
26342                 // remove comments
26343                 $data = preg_replace('/\%[^\n]*/', '', $data);
26344                 // extract the patterns part
26345                 preg_match('/\\\\patterns\{([^\}]*)\}/i', $data, $matches);
26346                 $data = trim(substr($matches[0], 10, -1));
26347                 // extract each pattern
26348                 $patterns_array = preg_split('/[\s]+/', $data);
26349                 // create new language array of patterns
26350                 $patterns = array();
26351                 foreach($patterns_array as $val) {
26352                         if (!$this->empty_string($val)) {
26353                                 $val = trim($val);
26354                                 $val = str_replace('\'', '\\\'', $val);
26355                                 $key = preg_replace('/[0-9]+/', '', $val);
26356                                 $patterns[$key] = $val;
26357                         }
26358                 }
26359                 return $patterns;
26360         }
26361 
26376         public function hyphenateText($text, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
26377                 $text = $this->unhtmlentities($text);
26378                 $word = array(); // last word
26379                 $txtarr = array(); // text to be returned
26380                 $intag = false; // true if we are inside an HTML tag
26381                 if (!is_array($patterns)) {
26382                         $patterns = $this->getHyphenPatternsFromTEX($patterns);
26383                 }
26384                 // get array of characters
26385                 $unichars = $this->UTF8StringToArray($text);
26386                 // for each char
26387                 foreach ($unichars as $char) {
26388                         if ((!$intag) AND $this->unicode->uni_type[$char] == 'L') {
26389                                 // letter character
26390                                 $word[] = $char;
26391                         } else {
26392                                 // other type of character
26393                                 if (!$this->empty_string($word)) {
26394                                         // hypenate the word
26395                                         $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
26396                                         $word = array();
26397                                 }
26398                                 $txtarr[] = $char;
26399                                 if (chr($char) == '<') {
26400                                         // we are inside an HTML tag
26401                                         $intag = true;
26402                                 } elseif ($intag AND (chr($char) == '>')) {
26403                                         // end of HTML tag
26404                                         $intag = false;
26405                                 }
26406                         }
26407                 }
26408                 if (!$this->empty_string($word)) {
26409                         // hypenate the word
26410                         $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
26411                 }
26412                 // convert char array to string and return
26413                 return $this->UTF8ArrSubString($txtarr);
26414         }
26415 
26422         public function setRasterizeVectorImages($mode) {
26423                 $this->rasterize_vector_images = $mode;
26424         }
26425 
26447         protected function getPathPaintOperator($style, $default='S') {
26448                 $op = '';
26449                 switch($style) {
26450                         case 'S':
26451                         case 'D': {
26452                                 $op = 'S';
26453                                 break;
26454                         }
26455                         case 's':
26456                         case 'd': {
26457                                 $op = 's';
26458                                 break;
26459                         }
26460                         case 'f':
26461                         case 'F': {
26462                                 $op = 'f';
26463                                 break;
26464                         }
26465                         case 'f*':
26466                         case 'F*': {
26467                                 $op = 'f*';
26468                                 break;
26469                         }
26470                         case 'B':
26471                         case 'FD':
26472                         case 'DF': {
26473                                 $op = 'B';
26474                                 break;
26475                         }
26476                         case 'B*':
26477                         case 'F*D':
26478                         case 'DF*': {
26479                                 $op = 'B*';
26480                                 break;
26481                         }
26482                         case 'b':
26483                         case 'fd':
26484                         case 'df': {
26485                                 $op = 'b';
26486                                 break;
26487                         }
26488                         case 'b*':
26489                         case 'f*d':
26490                         case 'df*': {
26491                                 $op = 'b*';
26492                                 break;
26493                         }
26494                         case 'CNZ': {
26495                                 $op = 'W n';
26496                                 break;
26497                         }
26498                         case 'CEO': {
26499                                 $op = 'W* n';
26500                                 break;
26501                         }
26502                         case 'n': {
26503                                 $op = 'n';
26504                                 break;
26505                         }
26506                         default: {
26507                                 if (!empty($default)) {
26508                                         $op = $this->getPathPaintOperator($default, '');
26509                                 } else {
26510                                         $op = '';
26511                                 }
26512                         }
26513                 }
26514                 return $op;
26515         }
26516 
26524         public function setFontSubsetting($enable=true) {
26525                 if ($this->pdfa_mode) {
26526                         $this->font_subsetting = false;
26527                 } else {
26528                         $this->font_subsetting = $enable ? true : false;
26529                 }
26530         }
26531 
26539         public function getFontSubsetting() {
26540                 return $this->font_subsetting;
26541         }
26542 
26552         public function stringLeftTrim($str, $replace='') {
26553                 return preg_replace('/^'.$this->re_space['p'].'+/'.$this->re_space['m'], $replace, $str);
26554         }
26555 
26565         public function stringRightTrim($str, $replace='') {
26566                 return preg_replace('/'.$this->re_space['p'].'+$/'.$this->re_space['m'], $replace, $str);
26567         }
26568 
26578         public function stringTrim($str, $replace='') {
26579                 $str = $this->stringLeftTrim($str, $replace);
26580                 $str = $this->stringRightTrim($str, $replace);
26581                 return $str;
26582         }
26583 
26591         public function isUnicodeFont() {
26592                 return (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0'));
26593         }
26594 
26603         public function getFontFamilyName($fontfamily) {
26604                 // remove spaces and symbols
26605                 $fontfamily = preg_replace('/[^a-z0-9\,]/', '', strtolower($fontfamily));
26606                 // extract all font names
26607                 $fontslist = preg_split('/[,]/', $fontfamily);
26608                 // find first valid font name
26609                 foreach ($fontslist as $font) {
26610                         // replace font variations
26611                         $font = preg_replace('/italic$/', 'I', $font);
26612                         $font = preg_replace('/oblique$/', 'I', $font);
26613                         $font = preg_replace('/bold([I]?)$/', 'B\\1', $font);
26614                         // replace common family names and core fonts
26615                         $pattern = array();
26616                         $replacement = array();
26617                         $pattern[] = '/^serif|^cursive|^fantasy|^timesnewroman/';
26618                         $replacement[] = 'times';
26619                         $pattern[] = '/^sansserif/';
26620                         $replacement[] = 'helvetica';
26621                         $pattern[] = '/^monospace/';
26622                         $replacement[] = 'courier';
26623                         $font = preg_replace($pattern, $replacement, $font);
26624                         if (in_array(strtolower($font), $this->fontlist) OR in_array($font, $this->fontkeys)) {
26625                                 return $font;
26626                         }
26627                 }
26628                 // return current font as default
26629                 return $this->CurrentFont['fontkey'];
26630         }
26631 
26646         public function startTemplate($w=0, $h=0, $group=false) {
26647                 if ($this->inxobj) {
26648                         // we are already inside an XObject template
26649                         return false;
26650                 }
26651                 $this->inxobj = true;
26652                 ++$this->n;
26653                 // XObject ID
26654                 $this->xobjid = 'XT'.$this->n;
26655                 // object ID
26656                 $this->xobjects[$this->xobjid] = array('n' => $this->n);
26657                 // store current graphic state
26658                 $this->xobjects[$this->xobjid]['gvars'] = $this->getGraphicVars();
26659                 // initialize data
26660                 $this->xobjects[$this->xobjid]['intmrk'] = 0;
26661                 $this->xobjects[$this->xobjid]['transfmrk'] = array();
26662                 $this->xobjects[$this->xobjid]['outdata'] = '';
26663                 $this->xobjects[$this->xobjid]['xobjects'] = array();
26664                 $this->xobjects[$this->xobjid]['images'] = array();
26665                 $this->xobjects[$this->xobjid]['fonts'] = array();
26666                 $this->xobjects[$this->xobjid]['annotations'] = array();
26667                 $this->xobjects[$this->xobjid]['extgstates'] = array();
26668                 $this->xobjects[$this->xobjid]['gradients'] = array();
26669                 $this->xobjects[$this->xobjid]['spot_colors'] = array();
26670                 // set new environment
26671                 $this->num_columns = 1;
26672                 $this->current_column = 0;
26673                 $this->SetAutoPageBreak(false);
26674                 if (($w === '') OR ($w <= 0)) {
26675                         $w = $this->w - $this->lMargin - $this->rMargin;
26676                 }
26677                 if (($h === '') OR ($h <= 0)) {
26678                         $h = $this->h - $this->tMargin - $this->bMargin;
26679                 }
26680                 $this->xobjects[$this->xobjid]['x'] = 0;
26681                 $this->xobjects[$this->xobjid]['y'] = 0;
26682                 $this->xobjects[$this->xobjid]['w'] = $w;
26683                 $this->xobjects[$this->xobjid]['h'] = $h;
26684                 $this->w = $w;
26685                 $this->h = $h;
26686                 $this->wPt = $this->w * $this->k;
26687                 $this->hPt = $this->h * $this->k;
26688                 $this->fwPt = $this->wPt;
26689                 $this->fhPt = $this->hPt;
26690                 $this->x = 0;
26691                 $this->y = 0;
26692                 $this->lMargin = 0;
26693                 $this->rMargin = 0;
26694                 $this->tMargin = 0;
26695                 $this->bMargin = 0;
26696                 // set group mode
26697                 $this->xobjects[$this->xobjid]['group'] = $group;
26698                 return $this->xobjid;
26699         }
26700 
26711         public function endTemplate() {
26712                 if (!$this->inxobj) {
26713                         // we are not inside a template
26714                         return false;
26715                 }
26716                 $this->inxobj = false;
26717                 // restore previous graphic state
26718                 $this->setGraphicVars($this->xobjects[$this->xobjid]['gvars'], true);
26719                 return $this->xobjid;
26720         }
26721 
26740         public function printTemplate($id, $x='', $y='', $w=0, $h=0, $align='', $palign='', $fitonpage=false) {
26741                 if (!isset($this->xobjects[$id])) {
26742                         $this->Error('The XObject Template \''.$id.'\' doesn\'t exist!');
26743                 }
26744                 if ($this->inxobj) {
26745                         if ($id == $this->xobjid) {
26746                                 // close current template
26747                                 $this->endTemplate();
26748                         } else {
26749                                 // use the template as resource for the template currently opened
26750                                 $this->xobjects[$this->xobjid]['xobjects'][$id] = $this->xobjects[$id];
26751                         }
26752                 }
26753                 // set default values
26754                 if ($x === '') {
26755                         $x = $this->x;
26756                 }
26757                 if ($y === '') {
26758                         $y = $this->y;
26759                 }
26760                 // check page for no-write regions and adapt page margins if necessary
26761                 list($x, $y) = $this->checkPageRegions($h, $x, $y);
26762                 $ow = $this->xobjects[$id]['w'];
26763                 $oh = $this->xobjects[$id]['h'];
26764                 // calculate template width and height on document
26765                 if (($w <= 0) AND ($h <= 0)) {
26766                         $w = $ow;
26767                         $h = $oh;
26768                 } elseif ($w <= 0) {
26769                         $w = $h * $ow / $oh;
26770                 } elseif ($h <= 0) {
26771                         $h = $w * $oh / $ow;
26772                 }
26773                 // fit the template on available space
26774                 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
26775                 // set page alignment
26776                 $rb_y = $y + $h;
26777                 // set alignment
26778                 if ($this->rtl) {
26779                         if ($palign == 'L') {
26780                                 $xt = $this->lMargin;
26781                         } elseif ($palign == 'C') {
26782                                 $xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
26783                         } elseif ($palign == 'R') {
26784                                 $xt = $this->w - $this->rMargin - $w;
26785                         } else {
26786                                 $xt = $x - $w;
26787                         }
26788                         $rb_x = $xt;
26789                 } else {
26790                         if ($palign == 'L') {
26791                                 $xt = $this->lMargin;
26792                         } elseif ($palign == 'C') {
26793                                 $xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
26794                         } elseif ($palign == 'R') {
26795                                 $xt = $this->w - $this->rMargin - $w;
26796                         } else {
26797                                 $xt = $x;
26798                         }
26799                         $rb_x = $xt + $w;
26800                 }
26801                 // print XObject Template + Transformation matrix
26802                 $this->StartTransform();
26803                 // translate and scale
26804                 $sx = ($w / $this->xobjects[$id]['w']);
26805                 $sy = ($h / $this->xobjects[$id]['h']);
26806                 $tm = array();
26807                 $tm[0] = $sx;
26808                 $tm[1] = 0;
26809                 $tm[2] = 0;
26810                 $tm[3] = $sy;
26811                 $tm[4] = $xt * $this->k;
26812                 $tm[5] = ($this->h - $h - $y) * $this->k;
26813                 $this->Transform($tm);
26814                 // set object
26815                 $this->_out('/'.$id.' Do');
26816                 $this->StopTransform();
26817                 // add annotations
26818                 if (!empty($this->xobjects[$id]['annotations'])) {
26819                         foreach ($this->xobjects[$id]['annotations'] as $annot) {
26820                                 // transform original coordinates
26821                                 $coordlt = $this->getTransformationMatrixProduct($tm, array(1, 0, 0, 1, ($annot['x'] * $this->k), (-$annot['y'] * $this->k)));
26822                                 $ax = ($coordlt[4] / $this->k);
26823                                 $ay = ($this->h - $h - ($coordlt[5] / $this->k));
26824                                 $coordrb = $this->getTransformationMatrixProduct($tm, array(1, 0, 0, 1, (($annot['x'] + $annot['w']) * $this->k), ((-$annot['y'] - $annot['h']) * $this->k)));
26825                                 $aw = ($coordrb[4] / $this->k) - $ax;
26826                                 $ah = ($this->h - $h - ($coordrb[5] / $this->k)) - $ay;
26827                                 $this->Annotation($ax, $ay, $aw, $ah, $annot['text'], $annot['opt'], $annot['spaces']);
26828                         }
26829                 }
26830                 // set pointer to align the next text/objects
26831                 switch($align) {
26832                         case 'T': {
26833                                 $this->y = $y;
26834                                 $this->x = $rb_x;
26835                                 break;
26836                         }
26837                         case 'M': {
26838                                 $this->y = $y + round($h/2);
26839                                 $this->x = $rb_x;
26840                                 break;
26841                         }
26842                         case 'B': {
26843                                 $this->y = $rb_y;
26844                                 $this->x = $rb_x;
26845                                 break;
26846                         }
26847                         case 'N': {
26848                                 $this->SetY($rb_y);
26849                                 break;
26850                         }
26851                         default:{
26852                                 break;
26853                         }
26854                 }
26855         }
26856 
26864         public function setFontStretching($perc=100) {
26865                 $this->font_stretching = $perc;
26866         }
26867 
26875         public function getFontStretching() {
26876                 return $this->font_stretching;
26877         }
26878 
26886         public function setFontSpacing($spacing=0) {
26887                 $this->font_spacing = $spacing;
26888         }
26889 
26897         public function getFontSpacing() {
26898                 return $this->font_spacing;
26899         }
26900 
26909         public function getPageRegions() {
26910                 return $this->page_regions;
26911         }
26912 
26924         public function setPageRegions($regions=array()) {
26925                 // empty current regions array
26926                 $this->page_regions = array();
26927                 // add regions
26928                 foreach ($regions as $data) {
26929                         $this->addPageRegion($data);
26930                 }
26931         }
26932 
26944         public function addPageRegion($region) {
26945                 if (!isset($region['page']) OR empty($region['page'])) {
26946                         $region['page'] = $this->page;
26947                 }
26948                 if (isset($region['xt']) AND isset($region['xb']) AND ($region['xt'] > 0) AND ($region['xb'] > 0)
26949                         AND isset($region['yt'])  AND isset($region['yb']) AND ($region['yt'] >= 0) AND ($region['yt'] < $region['yb'])
26950                         AND isset($region['side']) AND (($region['side'] == 'L') OR ($region['side'] == 'R'))) {
26951                         $this->page_regions[] = $region;
26952                 }
26953         }
26954 
26963         public function removePageRegion($key) {
26964                 if (isset($this->page_regions[$key])) {
26965                         unset($this->page_regions[$key]);
26966                 }
26967         }
26968 
26981         protected function checkPageRegions($h, $x, $y) {
26982                 // set default values
26983                 if ($x === '') {
26984                         $x = $this->x;
26985                 }
26986                 if ($y === '') {
26987                         $y = $this->y;
26988                 }
26989                 if (empty($this->page_regions)) {
26990                         // no page regions defined
26991                         return array($x, $y);
26992                 }
26993                 if (empty($h)) {
26994                         $h = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
26995                 }
26996                 // check for page break
26997                 if ($this->checkPageBreak($h, $y)) {
26998                         // the content will be printed on a new page
26999                         $x = $this->x;
27000                         $y = $this->y;
27001                 }
27002                 if ($this->num_columns > 1) {
27003                         if ($this->rtl) {
27004                                 $this->lMargin = $this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w'];
27005                         } else {
27006                                 $this->rMargin = $this->w - $this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w'];
27007                         }
27008                 } else {
27009                         if ($this->rtl) {
27010                                 $this->lMargin = $this->original_lMargin;
27011                         } else {
27012                                 $this->rMargin = $this->original_rMargin;
27013                         }
27014                 }
27015                 // adjust coordinates and page margins
27016                 foreach ($this->page_regions as $regid => $regdata) {
27017                         if ($regdata['page'] == $this->page) {
27018                                 // check region boundaries
27019                                 if (($y > ($regdata['yt'] - $h)) AND ($y <= $regdata['yb'])) {
27020                                         // Y is inside the region
27021                                         $minv = ($regdata['xb'] - $regdata['xt']) / ($regdata['yb'] - $regdata['yt']); // inverse of angular coefficient
27022                                         $yt = max($y, $regdata['yt']);
27023                                         $yb = min(($yt + $h), $regdata['yb']);
27024                                         $xt = (($yt - $regdata['yt']) * $minv) + $regdata['xt'];
27025                                         $xb = (($yb - $regdata['yt']) * $minv) + $regdata['xt'];
27026                                         if ($regdata['side'] == 'L') { // left side
27027                                                 $new_margin = max($xt, $xb);
27028                                                 if ($this->lMargin < $new_margin) {
27029                                                         if ($this->rtl) {
27030                                                                 // adjust left page margin
27031                                                                 $this->lMargin = $new_margin;
27032                                                         }
27033                                                         if ($x < $new_margin) {
27034                                                                 // adjust x position
27035                                                                 $x = $new_margin;
27036                                                                 if ($new_margin > ($this->w - $this->rMargin)) {
27037                                                                         // adjust y position
27038                                                                         $y = $regdata['yb'] - $h;
27039                                                                 }
27040                                                         }
27041                                                 }
27042                                         } elseif ($regdata['side'] == 'R') { // right side
27043                                                 $new_margin = min($xt, $xb);
27044                                                 if (($this->w - $this->rMargin) > $new_margin) {
27045                                                         if (!$this->rtl) {
27046                                                                 // adjust right page margin
27047                                                                 $this->rMargin = ($this->w - $new_margin);
27048                                                         }
27049                                                         if ($x > $new_margin) {
27050                                                                 // adjust x position
27051                                                                 $x = $new_margin;
27052                                                                 if ($new_margin > $this->lMargin) {
27053                                                                         // adjust y position
27054                                                                         $y = $regdata['yb'] - $h;
27055                                                                 }
27056                                                         }
27057                                                 }
27058                                         }
27059                                 }
27060                         }
27061                 }
27062                 return array($x, $y);
27063         }
27064 
27065         // --- SVG METHODS ---------------------------------------------------------
27066 
27084         public function ImageSVG($file, $x='', $y='', $w=0, $h=0, $link='', $align='', $palign='', $border=0, $fitonpage=false) {
27085                 if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) {
27086                         // convert SVG to raster image using GD or ImageMagick libraries
27087                         return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
27088                 }
27089                 if ($file{0} === '@') { // image from string
27090                         $this->svgdir = '';
27091                         $svgdata = substr($file, 1);
27092                 } else { // SVG file
27093                         $this->svgdir = dirname($file);
27094                         $svgdata = file_get_contents($file);
27095                 }
27096                 if ($svgdata === false) {
27097                         $this->Error('SVG file not found: '.$file);
27098                 }
27099                 if ($x === '') {
27100                         $x = $this->x;
27101                 }
27102                 if ($y === '') {
27103                         $y = $this->y;
27104                 }
27105                 // check page for no-write regions and adapt page margins if necessary
27106                 list($x, $y) = $this->checkPageRegions($h, $x, $y);
27107                 $k = $this->k;
27108                 $ox = 0;
27109                 $oy = 0;
27110                 $ow = $w;
27111                 $oh = $h;
27112                 $aspect_ratio_align = 'xMidYMid';
27113                 $aspect_ratio_ms = 'meet';
27114                 $regs = array();
27115                 // get original image width and height
27116                 preg_match('/<svg([^>]*)>/si', $svgdata, $regs);
27117                 if (isset($regs[1]) AND !empty($regs[1])) {
27118                         $tmp = array();
27119                         if (preg_match('/[\s]+x[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
27120                                 $ox = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false);
27121                         }
27122                         $tmp = array();
27123                         if (preg_match('/[\s]+y[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
27124                                 $oy = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false);
27125                         }
27126                         $tmp = array();
27127                         if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
27128                                 $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
27129                         }
27130                         $tmp = array();
27131                         if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
27132                                 $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
27133                         }
27134                         $tmp = array();
27135                         $view_box = array();
27136                         if (preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.\-]+)[\s]+([0-9\.\-]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $regs[1], $tmp)) {
27137                                 if (count($tmp) == 5) {
27138                                         array_shift($tmp);
27139                                         foreach ($tmp as $key => $val) {
27140                                                 $view_box[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false);
27141                                         }
27142                                         $ox = $view_box[0];
27143                                         $oy = $view_box[1];
27144                                 }
27145                                 // get aspect ratio
27146                                 $tmp = array();
27147                                 if (preg_match('/[\s]+preserveAspectRatio[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
27148                                         $aspect_ratio = preg_split('/[\s]+/si', $tmp[1]);
27149                                         switch (count($aspect_ratio)) {
27150                                                 case 3: {
27151                                                         $aspect_ratio_align = $aspect_ratio[1];
27152                                                         $aspect_ratio_ms = $aspect_ratio[2];
27153                                                         break;
27154                                                 }
27155                                                 case 2: {
27156                                                         $aspect_ratio_align = $aspect_ratio[0];
27157                                                         $aspect_ratio_ms = $aspect_ratio[1];
27158                                                         break;
27159                                                 }
27160                                                 case 1: {
27161                                                         $aspect_ratio_align = $aspect_ratio[0];
27162                                                         $aspect_ratio_ms = 'meet';
27163                                                         break;
27164                                                 }
27165                                         }
27166                                 }
27167                         }
27168                 }
27169                 // calculate image width and height on document
27170                 if (($w <= 0) AND ($h <= 0)) {
27171                         // convert image size to document unit
27172                         $w = $ow;
27173                         $h = $oh;
27174                 } elseif ($w <= 0) {
27175                         $w = $h * $ow / $oh;
27176                 } elseif ($h <= 0) {
27177                         $h = $w * $oh / $ow;
27178                 }
27179                 // fit the image on available space
27180                 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
27181                 if ($this->rasterize_vector_images) {
27182                         // convert SVG to raster image using GD or ImageMagick libraries
27183                         return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
27184                 }
27185                 // set alignment
27186                 $this->img_rb_y = $y + $h;
27187                 // set alignment
27188                 if ($this->rtl) {
27189                         if ($palign == 'L') {
27190                                 $ximg = $this->lMargin;
27191                         } elseif ($palign == 'C') {
27192                                 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
27193                         } elseif ($palign == 'R') {
27194                                 $ximg = $this->w - $this->rMargin - $w;
27195                         } else {
27196                                 $ximg = $x - $w;
27197                         }
27198                         $this->img_rb_x = $ximg;
27199                 } else {
27200                         if ($palign == 'L') {
27201                                 $ximg = $this->lMargin;
27202                         } elseif ($palign == 'C') {
27203                                 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
27204                         } elseif ($palign == 'R') {
27205                                 $ximg = $this->w - $this->rMargin - $w;
27206                         } else {
27207                                 $ximg = $x;
27208                         }
27209                         $this->img_rb_x = $ximg + $w;
27210                 }
27211                 // store current graphic vars
27212                 $gvars = $this->getGraphicVars();
27213                 // store SVG position and scale factors
27214                 $svgoffset_x = ($ximg - $ox) * $this->k;
27215                 $svgoffset_y = -($y - $oy) * $this->k;
27216                 if (isset($view_box[2]) AND ($view_box[2] > 0) AND ($view_box[3] > 0)) {
27217                         $ow = $view_box[2];
27218                         $oh = $view_box[3];
27219                 } else {
27220                         if ($ow <= 0) {
27221                                 $ow = $w;
27222                         }
27223                         if ($oh <= 0) {
27224                                 $oh = $h;
27225                         }
27226                 }
27227                 $svgscale_x = $w / $ow;
27228                 $svgscale_y = $h / $oh;
27229                 // scaling and alignment
27230                 if ($aspect_ratio_align != 'none') {
27231                         // store current scaling values
27232                         $svgscale_old_x = $svgscale_x;
27233                         $svgscale_old_y = $svgscale_y;
27234                         // force uniform scaling
27235                         if ($aspect_ratio_ms == 'slice') {
27236                                 // the entire viewport is covered by the viewBox
27237                                 if ($svgscale_x > $svgscale_y) {
27238                                         $svgscale_y = $svgscale_x;
27239                                 } elseif ($svgscale_x < $svgscale_y) {
27240                                         $svgscale_x = $svgscale_y;
27241                                 }
27242                         } else { // meet
27243                                 // the entire viewBox is visible within the viewport
27244                                 if ($svgscale_x < $svgscale_y) {
27245                                         $svgscale_y = $svgscale_x;
27246                                 } elseif ($svgscale_x > $svgscale_y) {
27247                                         $svgscale_x = $svgscale_y;
27248                                 }
27249                         }
27250                         // correct X alignment
27251                         switch (substr($aspect_ratio_align, 1, 3)) {
27252                                 case 'Min': {
27253                                         // do nothing
27254                                         break;
27255                                 }
27256                                 case 'Max': {
27257                                         $svgoffset_x += (($w * $this->k) - ($ow * $this->k * $svgscale_x));
27258                                         break;
27259                                 }
27260                                 default:
27261                                 case 'Mid': {
27262                                         $svgoffset_x += ((($w * $this->k) - ($ow * $this->k * $svgscale_x)) / 2);
27263                                         break;
27264                                 }
27265                         }
27266                         // correct Y alignment
27267                         switch (substr($aspect_ratio_align, 5)) {
27268                                 case 'Min': {
27269                                         // do nothing
27270                                         break;
27271                                 }
27272                                 case 'Max': {
27273                                         $svgoffset_y -= (($h * $this->k) - ($oh * $this->k * $svgscale_y));
27274                                         break;
27275                                 }
27276                                 default:
27277                                 case 'Mid': {
27278                                         $svgoffset_y -= ((($h * $this->k) - ($oh * $this->k * $svgscale_y)) / 2);
27279                                         break;
27280                                 }
27281                         }
27282                 }
27283                 // store current page break mode
27284                 $page_break_mode = $this->AutoPageBreak;
27285                 $page_break_margin = $this->getBreakMargin();
27286                 $cell_padding = $this->cell_padding;
27287                 $this->SetCellPadding(0);
27288                 $this->SetAutoPageBreak(false);
27289                 // save the current graphic state
27290                 $this->_out('q'.$this->epsmarker);
27291                 // set initial clipping mask
27292                 $this->Rect($x, $y, $w, $h, 'CNZ', array(), array());
27293                 // scale and translate
27294                 $e = $ox * $this->k * (1 - $svgscale_x);
27295                 $f = ($this->h - $oy) * $this->k * (1 - $svgscale_y);
27296                 $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $svgscale_x, 0, 0, $svgscale_y, $e + $svgoffset_x, $f + $svgoffset_y));
27297                 // creates a new XML parser to be used by the other XML functions
27298                 $this->parser = xml_parser_create('UTF-8');
27299                 // the following function allows to use parser inside object
27300                 xml_set_object($this->parser, $this);
27301                 // disable case-folding for this XML parser
27302                 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
27303                 // sets the element handler functions for the XML parser
27304                 xml_set_element_handler($this->parser, 'startSVGElementHandler', 'endSVGElementHandler');
27305                 // sets the character data handler function for the XML parser
27306                 xml_set_character_data_handler($this->parser, 'segSVGContentHandler');
27307                 // start parsing an XML document
27308                 if (!xml_parse($this->parser, $svgdata)) {
27309                         $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));
27310                         $this->Error($error_message);
27311                 }
27312                 // free this XML parser
27313                 xml_parser_free($this->parser);
27314                 // restore previous graphic state
27315                 $this->_out($this->epsmarker.'Q');
27316                 // restore graphic vars
27317                 $this->setGraphicVars($gvars);
27318                 $this->lasth = $gvars['lasth'];
27319                 if (!empty($border)) {
27320                         $bx = $this->x;
27321                         $by = $this->y;
27322                         $this->x = $ximg;
27323                         if ($this->rtl) {
27324                                 $this->x += $w;
27325                         }
27326                         $this->y = $y;
27327                         $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
27328                         $this->x = $bx;
27329                         $this->y = $by;
27330                 }
27331                 if ($link) {
27332                         $this->Link($ximg, $y, $w, $h, $link, 0);
27333                 }
27334                 // set pointer to align the next text/objects
27335                 switch($align) {
27336                         case 'T':{
27337                                 $this->y = $y;
27338                                 $this->x = $this->img_rb_x;
27339                                 break;
27340                         }
27341                         case 'M':{
27342                                 $this->y = $y + round($h/2);
27343                                 $this->x = $this->img_rb_x;
27344                                 break;
27345                         }
27346                         case 'B':{
27347                                 $this->y = $this->img_rb_y;
27348                                 $this->x = $this->img_rb_x;
27349                                 break;
27350                         }
27351                         case 'N':{
27352                                 $this->SetY($this->img_rb_y);
27353                                 break;
27354                         }
27355                         default:{
27356                                 // restore pointer to starting position
27357                                 $this->x = $gvars['x'];
27358                                 $this->y = $gvars['y'];
27359                                 $this->page = $gvars['page'];
27360                                 $this->current_column = $gvars['current_column'];
27361                                 $this->tMargin = $gvars['tMargin'];
27362                                 $this->bMargin = $gvars['bMargin'];
27363                                 $this->w = $gvars['w'];
27364                                 $this->h = $gvars['h'];
27365                                 $this->wPt = $gvars['wPt'];
27366                                 $this->hPt = $gvars['hPt'];
27367                                 $this->fwPt = $gvars['fwPt'];
27368                                 $this->fhPt = $gvars['fhPt'];
27369                                 break;
27370                         }
27371                 }
27372                 $this->endlinex = $this->img_rb_x;
27373                 // restore page break
27374                 $this->SetAutoPageBreak($page_break_mode, $page_break_margin);
27375                 $this->cell_padding = $cell_padding;
27376         }
27377 
27386         protected function getSVGTransformMatrix($attribute) {
27387                 // identity matrix
27388                 $tm = array(1, 0, 0, 1, 0, 0);
27389                 $transform = array();
27390                 if (preg_match_all('/(matrix|translate|scale|rotate|skewX|skewY)[\s]*\(([^\)]+)\)/si', $attribute, $transform, PREG_SET_ORDER) > 0) {
27391                         foreach ($transform as $key => $data) {
27392                                 if (!empty($data[2])) {
27393                                         $a = 1;
27394                                         $b = 0;
27395                                         $c = 0;
27396                                         $d = 1;
27397                                         $e = 0;
27398                                         $f = 0;
27399                                         $regs = array();
27400                                         switch ($data[1]) {
27401                                                 case 'matrix': {
27402                                                         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)) {
27403                                                                 $a = $regs[1];
27404                                                                 $b = $regs[2];
27405                                                                 $c = $regs[3];
27406                                                                 $d = $regs[4];
27407                                                                 $e = $regs[5];
27408                                                                 $f = $regs[6];
27409                                                         }
27410                                                         break;
27411                                                 }
27412                                                 case 'translate': {
27413                                                         if (preg_match('/([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)/si', $data[2], $regs)) {
27414                                                                 $e = $regs[1];
27415                                                                 $f = $regs[2];
27416                                                         } elseif (preg_match('/([a-z0-9\-\.]+)/si', $data[2], $regs)) {
27417                                                                 $e = $regs[1];
27418                                                         }
27419                                                         break;
27420                                                 }
27421                                                 case 'scale': {
27422                                                         if (preg_match('/([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)/si', $data[2], $regs)) {
27423                                                                 $a = $regs[1];
27424                                                                 $d = $regs[2];
27425                                                         } elseif (preg_match('/([a-z0-9\-\.]+)/si', $data[2], $regs)) {
27426                                                                 $a = $regs[1];
27427                                                                 $d = $a;
27428                                                         }
27429                                                         break;
27430                                                 }
27431                                                 case 'rotate': {
27432                                                         if (preg_match('/([0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)/si', $data[2], $regs)) {
27433                                                                 $ang = deg2rad($regs[1]);
27434                                                                 $x = $regs[2];
27435                                                                 $y = $regs[3];
27436                                                                 $a = cos($ang);
27437                                                                 $b = sin($ang);
27438                                                                 $c = -$b;
27439                                                                 $d = $a;
27440                                                                 $e = ($x * (1 - $a)) - ($y * $c);
27441                                                                 $f = ($y * (1 - $d)) - ($x * $b);
27442                                                         } elseif (preg_match('/([0-9\-\.]+)/si', $data[2], $regs)) {
27443                                                                 $ang = deg2rad($regs[1]);
27444                                                                 $a = cos($ang);
27445                                                                 $b = sin($ang);
27446                                                                 $c = -$b;
27447                                                                 $d = $a;
27448                                                                 $e = 0;
27449                                                                 $f = 0;
27450                                                         }
27451                                                         break;
27452                                                 }
27453                                                 case 'skewX': {
27454                                                         if (preg_match('/([0-9\-\.]+)/si', $data[2], $regs)) {
27455                                                                 $c = tan(deg2rad($regs[1]));
27456                                                         }
27457                                                         break;
27458                                                 }
27459                                                 case 'skewY': {
27460                                                         if (preg_match('/([0-9\-\.]+)/si', $data[2], $regs)) {
27461                                                                 $b = tan(deg2rad($regs[1]));
27462                                                         }
27463                                                         break;
27464                                                 }
27465                                         }
27466                                         $tm = $this->getTransformationMatrixProduct($tm, array($a, $b, $c, $d, $e, $f));
27467                                 }
27468                         }
27469                 }
27470                 return $tm;
27471         }
27472 
27482         protected function getTransformationMatrixProduct($ta, $tb) {
27483                 $tm = array();
27484                 $tm[0] = ($ta[0] * $tb[0]) + ($ta[2] * $tb[1]);
27485                 $tm[1] = ($ta[1] * $tb[0]) + ($ta[3] * $tb[1]);
27486                 $tm[2] = ($ta[0] * $tb[2]) + ($ta[2] * $tb[3]);
27487                 $tm[3] = ($ta[1] * $tb[2]) + ($ta[3] * $tb[3]);
27488                 $tm[4] = ($ta[0] * $tb[4]) + ($ta[2] * $tb[5]) + $ta[4];
27489                 $tm[5] = ($ta[1] * $tb[4]) + ($ta[3] * $tb[5]) + $ta[5];
27490                 return $tm;
27491         }
27492 
27500         protected function convertSVGtMatrix($tm) {
27501                 $a = $tm[0];
27502                 $b = -$tm[1];
27503                 $c = -$tm[2];
27504                 $d = $tm[3];
27505                 $e = $this->getHTMLUnitToUnits($tm[4], 1, $this->svgunit, false) * $this->k;
27506                 $f = -$this->getHTMLUnitToUnits($tm[5], 1, $this->svgunit, false) * $this->k;
27507                 $x = 0;
27508                 $y = $this->h * $this->k;
27509                 $e = ($x * (1 - $a)) - ($y * $c) + $e;
27510                 $f = ($y * (1 - $d)) - ($x * $b) + $f;
27511                 return array($a, $b, $c, $d, $e, $f);
27512         }
27513 
27520         protected function SVGTransform($tm) {
27521                 $this->Transform($this->convertSVGtMatrix($tm));
27522         }
27523 
27539         protected function setSVGStyles($svgstyle, $prevsvgstyle, $x=0, $y=0, $w=1, $h=1, $clip_function='', $clip_params=array()) {
27540                 $objstyle = '';
27541                 $minlen = (0.01 / $this->k); // minimum acceptable length (3 point)
27542                 if (!isset($svgstyle['opacity'])) {
27543                         return $objstyle;
27544                 }
27545                 // clip-path
27546                 $regs = array();
27547                 if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['clip-path'], $regs)) {
27548                         $clip_path = $this->svgclippaths[$regs[1]];
27549                         foreach ($clip_path as $cp) {
27550                                 $this->startSVGElementHandler('clip-path', $cp['name'], $cp['attribs'], $cp['tm']);
27551                         }
27552                 }
27553                 // opacity
27554                 if ($svgstyle['opacity'] != 1) {
27555                         $this->SetAlpha($svgstyle['opacity']);
27556                 }
27557                 // color
27558                 $fill_color = $this->convertHTMLColorToDec($svgstyle['color']);
27559                 $this->SetFillColorArray($fill_color);
27560                 // text color
27561                 $text_color = $this->convertHTMLColorToDec($svgstyle['text-color']);
27562                 $this->SetTextColorArray($text_color);
27563                 // clip
27564                 if (preg_match('/rect\(([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)\)/si', $svgstyle['clip'], $regs)) {
27565                         $top = (isset($regs[1])?$this->getHTMLUnitToUnits($regs[1], 0, $this->svgunit, false):0);
27566                         $right = (isset($regs[2])?$this->getHTMLUnitToUnits($regs[2], 0, $this->svgunit, false):0);
27567                         $bottom = (isset($regs[3])?$this->getHTMLUnitToUnits($regs[3], 0, $this->svgunit, false):0);
27568                         $left = (isset($regs[4])?$this->getHTMLUnitToUnits($regs[4], 0, $this->svgunit, false):0);
27569                         $cx = $x + $left;
27570                         $cy = $y + $top;
27571                         $cw = $w - $left - $right;
27572                         $ch = $h - $top - $bottom;
27573                         if ($svgstyle['clip-rule'] == 'evenodd') {
27574                                 $clip_rule = 'CNZ';
27575                         } else {
27576                                 $clip_rule = 'CEO';
27577                         }
27578                         $this->Rect($cx, $cy, $cw, $ch, $clip_rule, array(), array());
27579                 }
27580                 // fill
27581                 $regs = array();
27582                 if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['fill'], $regs)) {
27583                         // gradient
27584                         $gradient = $this->svggradients[$regs[1]];
27585                         if (isset($gradient['xref'])) {
27586                                 // reference to another gradient definition
27587                                 $newgradient = $this->svggradients[$gradient['xref']];
27588                                 $newgradient['coords'] = $gradient['coords'];
27589                                 $newgradient['mode'] = $gradient['mode'];
27590                                 $newgradient['gradientUnits'] = $gradient['gradientUnits'];
27591                                 if (isset($gradient['gradientTransform'])) {
27592                                         $newgradient['gradientTransform'] = $gradient['gradientTransform'];
27593                                 }
27594                                 $gradient = $newgradient;
27595                         }
27596                         //save current Graphic State
27597                         $this->_out('q');
27598                         //set clipping area
27599                         if (!empty($clip_function) AND method_exists($this, $clip_function)) {
27600                                 $bbox = call_user_func_array(array($this, $clip_function), $clip_params);
27601                                 if (is_array($bbox) AND (count($bbox) == 4)) {
27602                                         list($x, $y, $w, $h) = $bbox;
27603                                 }
27604                         }
27605                         if ($gradient['mode'] == 'measure') {
27606                                 if (isset($gradient['gradientTransform']) AND !empty($gradient['gradientTransform'])) {
27607                                         $gtm = $gradient['gradientTransform'];
27608                                         // apply transformation matrix
27609                                         $xa = ($gtm[0] * $gradient['coords'][0]) + ($gtm[2] * $gradient['coords'][1]) + $gtm[4];
27610                                         $ya = ($gtm[1] * $gradient['coords'][0]) + ($gtm[3] * $gradient['coords'][1]) + $gtm[5];
27611                                         $xb = ($gtm[0] * $gradient['coords'][2]) + ($gtm[2] * $gradient['coords'][3]) + $gtm[4];
27612                                         $yb = ($gtm[1] * $gradient['coords'][2]) + ($gtm[3] * $gradient['coords'][3]) + $gtm[5];
27613                                         if (isset($gradient['coords'][4])) {
27614                                                 $gradient['coords'][4] = sqrt(pow(($gtm[0] * $gradient['coords'][4]), 2) + pow(($gtm[1] * $gradient['coords'][4]), 2));
27615                                         }
27616                                         $gradient['coords'][0] = $xa;
27617                                         $gradient['coords'][1] = $ya;
27618                                         $gradient['coords'][2] = $xb;
27619                                         $gradient['coords'][3] = $yb;
27620                                 }
27621                                 // convert SVG coordinates to user units
27622                                 $gradient['coords'][0] = $this->getHTMLUnitToUnits($gradient['coords'][0], 0, $this->svgunit, false);
27623                                 $gradient['coords'][1] = $this->getHTMLUnitToUnits($gradient['coords'][1], 0, $this->svgunit, false);
27624                                 $gradient['coords'][2] = $this->getHTMLUnitToUnits($gradient['coords'][2], 0, $this->svgunit, false);
27625                                 $gradient['coords'][3] = $this->getHTMLUnitToUnits($gradient['coords'][3], 0, $this->svgunit, false);
27626                                 if (isset($gradient['coords'][4])) {
27627                                         $gradient['coords'][4] = $this->getHTMLUnitToUnits($gradient['coords'][4], 0, $this->svgunit, false);
27628                                 }
27629                                 // shift units
27630                                 if ($gradient['gradientUnits'] == 'objectBoundingBox') {
27631                                         // convert to SVG coordinate system
27632                                         $gradient['coords'][0] += $x;
27633                                         $gradient['coords'][1] += $y;
27634                                         $gradient['coords'][2] += $x;
27635                                         $gradient['coords'][3] += $y;
27636                                 }
27637                                 if ($w <= $minlen) {
27638                                         $w = $minlen;
27639                                 }
27640                                 if ($h <= $minlen) {
27641                                         $h = $minlen;
27642                                 }
27643                                 // calculate percentages
27644                                 $gradient['coords'][0] = ($gradient['coords'][0] - $x) / $w;
27645                                 $gradient['coords'][1] = ($gradient['coords'][1] - $y) / $h;
27646                                 $gradient['coords'][2] = ($gradient['coords'][2] - $x) / $w;
27647                                 $gradient['coords'][3] = ($gradient['coords'][3] - $y) / $h;
27648                                 if (isset($gradient['coords'][4])) {
27649                                         $gradient['coords'][4] /= $w;
27650                                 }
27651                         } elseif ($gradient['mode'] == 'percentage') {
27652                                 foreach($gradient['coords'] as $key => $val) {
27653                                         $gradient['coords'][$key] = (intval($val) / 100);
27654                                 }
27655                         }
27656                         // fix values
27657                         foreach($gradient['coords'] as $key => $val) {
27658                                 if ($val < 0) {
27659                                         $gradient['coords'][$key] = 0;
27660                                 } elseif ($val > 1) {
27661                                         $gradient['coords'][$key] = 1;
27662                                 }
27663                         }
27664                         if (($gradient['type'] == 2) AND ($gradient['coords'][0] == $gradient['coords'][2]) AND ($gradient['coords'][1] == $gradient['coords'][3])) {
27665                                 // single color (no shading)
27666                                 $gradient['coords'][0] = 1;
27667                                 $gradient['coords'][1] = 0;
27668                                 $gradient['coords'][2] = 0.999;
27669                                 $gradient['coords'][3] = 0;
27670                         }
27671                         // swap Y coordinates
27672                         $tmp = $gradient['coords'][1];
27673                         $gradient['coords'][1] = $gradient['coords'][3];
27674                         $gradient['coords'][3] = $tmp;
27675                         // set transformation map for gradient
27676                         if ($gradient['type'] == 3) {
27677                                 // gradient is always circular
27678                                 $cy = $this->h - $y - ($gradient['coords'][1] * ($w + $h));
27679                                 $this->_out(sprintf('%.3F 0 0 %.3F %.3F %.3F cm', $w*$this->k, $w*$this->k, $x*$this->k, $cy*$this->k));
27680                         } else {
27681                                 $this->_out(sprintf('%.3F 0 0 %.3F %.3F %.3F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k));
27682                         }
27683                         if (count($gradient['stops']) > 1) {
27684                                 $this->Gradient($gradient['type'], $gradient['coords'], $gradient['stops'], array(), false);
27685                         }
27686                 } elseif ($svgstyle['fill'] != 'none') {
27687                         $fill_color = $this->convertHTMLColorToDec($svgstyle['fill']);
27688                         if ($svgstyle['fill-opacity'] != 1) {
27689                                 $this->SetAlpha($svgstyle['fill-opacity']);
27690                         }
27691                         $this->SetFillColorArray($fill_color);
27692                         if ($svgstyle['fill-rule'] == 'evenodd') {
27693                                 $objstyle .= 'F*';
27694                         } else {
27695                                 $objstyle .= 'F';
27696                         }
27697                 }
27698                 // stroke
27699                 if ($svgstyle['stroke'] != 'none') {
27700                         $stroke_style = array(
27701                                 'color' => $this->convertHTMLColorToDec($svgstyle['stroke']),
27702                                 'width' => $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false),
27703                                 'cap' => $svgstyle['stroke-linecap'],
27704                                 'join' => $svgstyle['stroke-linejoin']
27705                                 );
27706                         if (isset($svgstyle['stroke-dasharray']) AND !empty($svgstyle['stroke-dasharray']) AND ($svgstyle['stroke-dasharray'] != 'none')) {
27707                                 $stroke_style['dash'] = $svgstyle['stroke-dasharray'];
27708                         }
27709                         $this->SetLineStyle($stroke_style);
27710                         $objstyle .= 'D';
27711                 }
27712                 // font
27713                 $regs = array();
27714                 if (!empty($svgstyle['font'])) {
27715                         if (preg_match('/font-family[\s]*:[\s]*([^\;\"]*)/si', $svgstyle['font'], $regs)) {
27716                                 $font_family = $this->getFontFamilyName($regs[1]);
27717                         } else {
27718                                 $font_family = $svgstyle['font-family'];
27719                         }
27720                         if (preg_match('/font-size[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
27721                                 $font_size = trim($regs[1]);
27722                         } else {
27723                                 $font_size = $svgstyle['font-size'];
27724                         }
27725                         if (preg_match('/font-style[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
27726                                 $font_style = trim($regs[1]);
27727                         } else {
27728                                 $font_style = $svgstyle['font-style'];
27729                         }
27730                         if (preg_match('/font-weight[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
27731                                 $font_weight = trim($regs[1]);
27732                         } else {
27733                                 $font_weight = $svgstyle['font-weight'];
27734                         }
27735                         if (preg_match('/font-stretch[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
27736                                 $font_stretch = trim($regs[1]);
27737                         } else {
27738                                 $font_stretch = $svgstyle['font-stretch'];
27739                         }
27740                         if (preg_match('/letter-spacing[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
27741                                 $font_spacing = trim($regs[1]);
27742                         } else {
27743                                 $font_spacing = $svgstyle['letter-spacing'];
27744                         }
27745                 } else {
27746                         $font_family = $this->getFontFamilyName($svgstyle['font-family']);
27747                         $font_size = $svgstyle['font-size'];
27748                         $font_style = $svgstyle['font-style'];
27749                         $font_weight = $svgstyle['font-weight'];
27750                         $font_stretch = $svgstyle['font-stretch'];
27751                         $font_spacing = $svgstyle['letter-spacing'];
27752                 }
27753                 $font_size = $this->getHTMLUnitToUnits($font_size, $prevsvgstyle['font-size'], $this->svgunit, false) * $this->k;
27754                 $font_stretch = $this->getCSSFontStretching($font_stretch, $svgstyle['font-stretch']);
27755                 $font_spacing = $this->getCSSFontSpacing($font_spacing, $svgstyle['letter-spacing']);
27756                 switch ($font_style) {
27757                         case 'italic': {
27758                                 $font_style = 'I';
27759                                 break;
27760                         }
27761                         case 'oblique': {
27762                                 $font_style = 'I';
27763                                 break;
27764                         }
27765                         default:
27766                         case 'normal': {
27767                                 $font_style = '';
27768                                 break;
27769                         }
27770                 }
27771                 switch ($font_weight) {
27772                         case 'bold':
27773                         case 'bolder': {
27774                                 $font_style .= 'B';
27775                                 break;
27776                         }
27777                 }
27778                 switch ($svgstyle['text-decoration']) {
27779                         case 'underline': {
27780                                 $font_style .= 'U';
27781                                 break;
27782                         }
27783                         case 'overline': {
27784                                 $font_style .= 'O';
27785                                 break;
27786                         }
27787                         case 'line-through': {
27788                                 $font_style .= 'D';
27789                                 break;
27790                         }
27791                         default:
27792                         case 'none': {
27793                                 break;
27794                         }
27795                 }
27796                 $this->SetFont($font_family, $font_style, $font_size);
27797                 $this->setFontStretching($font_stretch);
27798                 $this->setFontSpacing($font_spacing);
27799                 return $objstyle;
27800         }
27801 
27820         protected function SVGPath($d, $style='') {
27821                 // set fill/stroke style
27822                 $op = $this->getPathPaintOperator($style, '');
27823                 if (empty($op)) {
27824                         return;
27825                 }
27826                 $paths = array();
27827                 $d = preg_replace('/([0-9ACHLMQSTVZ])([\-\+])/si', '\\1 \\2', $d);
27828                 preg_match_all('/([ACHLMQSTVZ])[\s]*([^ACHLMQSTVZ\"]*)/si', $d, $paths, PREG_SET_ORDER);
27829                 $x = 0;
27830                 $y = 0;
27831                 $x1 = 0;
27832                 $y1 = 0;
27833                 $x2 = 0;
27834                 $y2 = 0;
27835                 $xmin = 2147483647;
27836                 $xmax = 0;
27837                 $ymin = 2147483647;
27838                 $ymax = 0;
27839                 $relcoord = false;
27840                 $minlen = (0.01 / $this->k); // minimum acceptable length (3 point)
27841                 $firstcmd = true; // used to print first point
27842                 // draw curve pieces
27843                 foreach ($paths as $key => $val) {
27844                         // get curve type
27845                         $cmd = trim($val[1]);
27846                         if (strtolower($cmd) == $cmd) {
27847                                 // use relative coordinated instead of absolute
27848                                 $relcoord = true;
27849                                 $xoffset = $x;
27850                                 $yoffset = $y;
27851                         } else {
27852                                 $relcoord = false;
27853                                 $xoffset = 0;
27854                                 $yoffset = 0;
27855                         }
27856                         $params = array();
27857                         if (isset($val[2])) {
27858                                 // get curve parameters
27859                                 $rawparams = preg_split('/([\,\s]+)/si', trim($val[2]));
27860                                 $params = array();
27861                                 foreach ($rawparams as $ck => $cp) {
27862                                         $params[$ck] = $this->getHTMLUnitToUnits($cp, 0, $this->svgunit, false);
27863                                         if (abs($params[$ck]) < $minlen) {
27864                                                 // aproximate little values to zero
27865                                                 $params[$ck] = 0;
27866                                         }
27867                                 }
27868                         }
27869                         // store current origin point
27870                         $x0 = $x;
27871                         $y0 = $y;
27872                         switch (strtoupper($cmd)) {
27873                                 case 'M': { // moveto
27874                                         foreach ($params as $ck => $cp) {
27875                                                 if (($ck % 2) == 0) {
27876                                                         $x = $cp + $xoffset;
27877                                                 } else {
27878                                                         $y = $cp + $yoffset;
27879                                                         if ($firstcmd OR (abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
27880                                                                 if ($ck == 1) {
27881                                                                         $this->_outPoint($x, $y);
27882                                                                         $firstcmd = false;
27883                                                                 } else {
27884                                                                         $this->_outLine($x, $y);
27885                                                                 }
27886                                                         }
27887                                                         $xmin = min($xmin, $x);
27888                                                         $ymin = min($ymin, $y);
27889                                                         $xmax = max($xmax, $x);
27890                                                         $ymax = max($ymax, $y);
27891                                                         if ($relcoord) {
27892                                                                 $xoffset = $x;
27893                                                                 $yoffset = $y;
27894                                                         }
27895                                                 }
27896                                         }
27897                                         break;
27898                                 }
27899                                 case 'L': { // lineto
27900                                         foreach ($params as $ck => $cp) {
27901                                                 if (($ck % 2) == 0) {
27902                                                         $x = $cp + $xoffset;
27903                                                 } else {
27904                                                         $y = $cp + $yoffset;
27905                                                         if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
27906                                                                 $this->_outLine($x, $y);
27907                                                         }
27908                                                         $xmin = min($xmin, $x);
27909                                                         $ymin = min($ymin, $y);
27910                                                         $xmax = max($xmax, $x);
27911                                                         $ymax = max($ymax, $y);
27912                                                         if ($relcoord) {
27913                                                                 $xoffset = $x;
27914                                                                 $yoffset = $y;
27915                                                         }
27916                                                 }
27917                                         }
27918                                         break;
27919                                 }
27920                                 case 'H': { // horizontal lineto
27921                                         foreach ($params as $ck => $cp) {
27922                                                 $x = $cp + $xoffset;
27923                                                 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
27924                                                         $this->_outLine($x, $y);
27925                                                 }
27926                                                 $xmin = min($xmin, $x);
27927                                                 $xmax = max($xmax, $x);
27928                                                 if ($relcoord) {
27929                                                         $xoffset = $x;
27930                                                 }
27931                                         }
27932                                         break;
27933                                 }
27934                                 case 'V': { // vertical lineto
27935                                         foreach ($params as $ck => $cp) {
27936                                                 $y = $cp + $yoffset;
27937                                                 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
27938                                                         $this->_outLine($x, $y);
27939                                                 }
27940                                                 $ymin = min($ymin, $y);
27941                                                 $ymax = max($ymax, $y);
27942                                                 if ($relcoord) {
27943                                                         $yoffset = $y;
27944                                                 }
27945                                         }
27946                                         break;
27947                                 }
27948                                 case 'C': { // curveto
27949                                         foreach ($params as $ck => $cp) {
27950                                                 $params[$ck] = $cp;
27951                                                 if ((($ck + 1) % 6) == 0) {
27952                                                         $x1 = $params[($ck - 5)] + $xoffset;
27953                                                         $y1 = $params[($ck - 4)] + $yoffset;
27954                                                         $x2 = $params[($ck - 3)] + $xoffset;
27955                                                         $y2 = $params[($ck - 2)] + $yoffset;
27956                                                         $x = $params[($ck - 1)] + $xoffset;
27957                                                         $y = $params[($ck)] + $yoffset;
27958                                                         $this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
27959                                                         $xmin = min($xmin, $x, $x1, $x2);
27960                                                         $ymin = min($ymin, $y, $y1, $y2);
27961                                                         $xmax = max($xmax, $x, $x1, $x2);
27962                                                         $ymax = max($ymax, $y, $y1, $y2);
27963                                                         if ($relcoord) {
27964                                                                 $xoffset = $x;
27965                                                                 $yoffset = $y;
27966                                                         }
27967                                                 }
27968                                         }
27969                                         break;
27970                                 }
27971                                 case 'S': { // shorthand/smooth curveto
27972                                         foreach ($params as $ck => $cp) {
27973                                                 $params[$ck] = $cp;
27974                                                 if ((($ck + 1) % 4) == 0) {
27975                                                         if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'C') OR (strtoupper($paths[($key - 1)][1]) == 'S'))) {
27976                                                                 $x1 = (2 * $x) - $x2;
27977                                                                 $y1 = (2 * $y) - $y2;
27978                                                         } else {
27979                                                                 $x1 = $x;
27980                                                                 $y1 = $y;
27981                                                         }
27982                                                         $x2 = $params[($ck - 3)] + $xoffset;
27983                                                         $y2 = $params[($ck - 2)] + $yoffset;
27984                                                         $x = $params[($ck - 1)] + $xoffset;
27985                                                         $y = $params[($ck)] + $yoffset;
27986                                                         $this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
27987                                                         $xmin = min($xmin, $x, $x1, $x2);
27988                                                         $ymin = min($ymin, $y, $y1, $y2);
27989                                                         $xmax = max($xmax, $x, $x1, $x2);
27990                                                         $ymax = max($ymax, $y, $y1, $y2);
27991                                                         if ($relcoord) {
27992                                                                 $xoffset = $x;
27993                                                                 $yoffset = $y;
27994                                                         }
27995                                                 }
27996                                         }
27997                                         break;
27998                                 }
27999                                 case 'Q': { // quadratic Bézier curveto
28000                                         foreach ($params as $ck => $cp) {
28001                                                 $params[$ck] = $cp;
28002                                                 if ((($ck + 1) % 4) == 0) {
28003                                                         // convert quadratic points to cubic points
28004                                                         $x1 = $params[($ck - 3)] + $xoffset;
28005                                                         $y1 = $params[($ck - 2)] + $yoffset;
28006                                                         $xa = ($x + (2 * $x1)) / 3;
28007                                                         $ya = ($y + (2 * $y1)) / 3;
28008                                                         $x = $params[($ck - 1)] + $xoffset;
28009                                                         $y = $params[($ck)] + $yoffset;
28010                                                         $xb = ($x + (2 * $x1)) / 3;
28011                                                         $yb = ($y + (2 * $y1)) / 3;
28012                                                         $this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
28013                                                         $xmin = min($xmin, $x, $xa, $xb);
28014                                                         $ymin = min($ymin, $y, $ya, $yb);
28015                                                         $xmax = max($xmax, $x, $xa, $xb);
28016                                                         $ymax = max($ymax, $y, $ya, $yb);
28017                                                         if ($relcoord) {
28018                                                                 $xoffset = $x;
28019                                                                 $yoffset = $y;
28020                                                         }
28021                                                 }
28022                                         }
28023                                         break;
28024                                 }
28025                                 case 'T': { // shorthand/smooth quadratic Bézier curveto
28026                                         foreach ($params as $ck => $cp) {
28027                                                 $params[$ck] = $cp;
28028                                                 if (($ck % 2) != 0) {
28029                                                         if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'Q') OR (strtoupper($paths[($key - 1)][1]) == 'T'))) {
28030                                                                 $x1 = (2 * $x) - $x1;
28031                                                                 $y1 = (2 * $y) - $y1;
28032                                                         } else {
28033                                                                 $x1 = $x;
28034                                                                 $y1 = $y;
28035                                                         }
28036                                                         // convert quadratic points to cubic points
28037                                                         $xa = ($x + (2 * $x1)) / 3;
28038                                                         $ya = ($y + (2 * $y1)) / 3;
28039                                                         $x = $params[($ck - 1)] + $xoffset;
28040                                                         $y = $params[($ck)] + $yoffset;
28041                                                         $xb = ($x + (2 * $x1)) / 3;
28042                                                         $yb = ($y + (2 * $y1)) / 3;
28043                                                         $this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
28044                                                         $xmin = min($xmin, $x, $xa, $xb);
28045                                                         $ymin = min($ymin, $y, $ya, $yb);
28046                                                         $xmax = max($xmax, $x, $xa, $xb);
28047                                                         $ymax = max($ymax, $y, $ya, $yb);
28048                                                         if ($relcoord) {
28049                                                                 $xoffset = $x;
28050                                                                 $yoffset = $y;
28051                                                         }
28052                                                 }
28053                                         }
28054                                         break;
28055                                 }
28056                                 case 'A': { // elliptical arc
28057                                         foreach ($params as $ck => $cp) {
28058                                                 $params[$ck] = $cp;
28059                                                 if ((($ck + 1) % 7) == 0) {
28060                                                         $x0 = $x;
28061                                                         $y0 = $y;
28062                                                         $rx = abs($params[($ck - 6)]);
28063                                                         $ry = abs($params[($ck - 5)]);
28064                                                         $ang = -$rawparams[($ck - 4)];
28065                                                         $angle = deg2rad($ang);
28066                                                         $fa = $rawparams[($ck - 3)]; // large-arc-flag
28067                                                         $fs = $rawparams[($ck - 2)]; // sweep-flag
28068                                                         $x = $params[($ck - 1)] + $xoffset;
28069                                                         $y = $params[$ck] + $yoffset;
28070                                                         if ((abs($x0 - $x) < $minlen) AND (abs($y0 - $y) < $minlen)) {
28071                                                                 // endpoints are almost identical
28072                                                                 $xmin = min($xmin, $x);
28073                                                                 $ymin = min($ymin, $y);
28074                                                                 $xmax = max($xmax, $x);
28075                                                                 $ymax = max($ymax, $y);
28076                                                         } else {
28077                                                                 $cos_ang = cos($angle);
28078                                                                 $sin_ang = sin($angle);
28079                                                                 $a = (($x0 - $x) / 2);
28080                                                                 $b = (($y0 - $y) / 2);
28081                                                                 $xa = ($a * $cos_ang) - ($b * $sin_ang);
28082                                                                 $ya = ($a * $sin_ang) + ($b * $cos_ang);
28083                                                                 $rx2 = $rx * $rx;
28084                                                                 $ry2 = $ry * $ry;
28085                                                                 $xa2 = $xa * $xa;
28086                                                                 $ya2 = $ya * $ya;
28087                                                                 $delta = ($xa2 / $rx2) + ($ya2 / $ry2);
28088                                                                 if ($delta > 1) {
28089                                                                         $rx *= sqrt($delta);
28090                                                                         $ry *= sqrt($delta);
28091                                                                         $rx2 = $rx * $rx;
28092                                                                         $ry2 = $ry * $ry;
28093                                                                 }
28094                                                                 $numerator = (($rx2 * $ry2) - ($rx2 * $ya2) - ($ry2 * $xa2));
28095                                                                 if ($numerator < 0) {
28096                                                                         $root = 0;
28097                                                                 } else {
28098                                                                         $root = sqrt($numerator / (($rx2 * $ya2) + ($ry2 * $xa2)));
28099                                                                 }
28100                                                                 if ($fa == $fs){
28101                                                                         $root *= -1;
28102                                                                 }
28103                                                                 $cax = $root * (($rx * $ya) / $ry);
28104                                                                 $cay = -$root * (($ry * $xa) / $rx);
28105                                                                 // coordinates of ellipse center
28106                                                                 $cx = ($cax * $cos_ang) - ($cay * $sin_ang) + (($x0 + $x) / 2);
28107                                                                 $cy = ($cax * $sin_ang) + ($cay * $cos_ang) + (($y0 + $y) / 2);
28108                                                                 // get angles
28109                                                                 $angs = $this->getVectorsAngle(1, 0, (($xa - $cax) / $rx), (($cay - $ya) / $ry));
28110                                                                 $dang = $this->getVectorsAngle((($xa - $cax) / $rx), (($ya - $cay) / $ry), ((-$xa - $cax) / $rx), ((-$ya - $cay) / $ry));
28111                                                                 if (($fs == 0) AND ($dang > 0)) {
28112                                                                         $dang -= (2 * M_PI);
28113                                                                 } elseif (($fs == 1) AND ($dang < 0)) {
28114                                                                         $dang += (2 * M_PI);
28115                                                                 }
28116                                                                 $angf = $angs - $dang;
28117                                                                 if ((($fs == 0) AND ($angs > $angf)) OR (($fs == 1) AND ($angs < $angf))) {
28118                                                                         // reverse angles
28119                                                                         $tmp = $angs;
28120                                                                         $angs = $angf;
28121                                                                         $angf = $tmp;
28122                                                                 }
28123                                                                 $angs = round(rad2deg($angs), 6);
28124                                                                 $angf = round(rad2deg($angf), 6);
28125                                                                 // covent angles to positive values
28126                                                                 if (($angs < 0) AND ($angf < 0)) {
28127                                                                         $angs += 360;
28128                                                                         $angf += 360;
28129                                                                 }
28130                                                                 $pie = false;
28131                                                                 if (($key == 0) AND (isset($paths[($key + 1)][1])) AND (trim($paths[($key + 1)][1]) == 'z')) {
28132                                                                         $pie = true;
28133                                                                 }
28134                                                                 list($axmin, $aymin, $axmax, $aymax) = $this->_outellipticalarc($cx, $cy, $rx, $ry, $ang, $angs, $angf, $pie, 2, false, ($fs == 0), true);
28135                                                                 $xmin = min($xmin, $x, $axmin);
28136                                                                 $ymin = min($ymin, $y, $aymin);
28137                                                                 $xmax = max($xmax, $x, $axmax);
28138                                                                 $ymax = max($ymax, $y, $aymax);
28139                                                         }
28140                                                         if ($relcoord) {
28141                                                                 $xoffset = $x;
28142                                                                 $yoffset = $y;
28143                                                         }
28144                                                 }
28145                                         }
28146                                         break;
28147                                 }
28148                                 case 'Z': {
28149                                         $this->_out('h');
28150                                         break;
28151                                 }
28152                         }
28153                         $firstcmd = false;
28154                 } // end foreach
28155                 if (!empty($op)) {
28156                         $this->_out($op);
28157                 }
28158                 return array($xmin, $ymin, ($xmax - $xmin), ($ymax - $ymin));
28159         }
28160 
28171         protected function getVectorsAngle($x1, $y1, $x2, $y2) {
28172                 $dprod = ($x1 * $x2) + ($y1 * $y2);
28173                 $dist1 = sqrt(($x1 * $x1) + ($y1 * $y1));
28174                 $dist2 = sqrt(($x2 * $x2) + ($y2 * $y2));
28175                 $angle = acos($dprod / ($dist1 * $dist2));
28176                 if (is_nan($angle)) {
28177                         $angle = M_PI;
28178                 }
28179                 if ((($x1 * $y2) - ($x2 * $y1)) < 0) {
28180                         $angle *= -1;
28181                 }
28182                 return $angle;
28183         }
28184 
28195         protected function startSVGElementHandler($parser, $name, $attribs, $ctm=array()) {
28196                 // check if we are in clip mode
28197                 if ($this->svgclipmode) {
28198                         $this->svgclippaths[$this->svgclipid][] = array('name' => $name, 'attribs' => $attribs, 'tm' => $this->svgcliptm[$this->svgclipid]);
28199                         return;
28200                 }
28201                 if ($this->svgdefsmode AND !in_array($name, array('clipPath', 'linearGradient', 'radialGradient', 'stop'))) {
28202                         if (!isset($attribs['id'])) {
28203                                 $attribs['id'] = 'DF_'.(count($this->svgdefs) + 1);
28204                         }
28205                         $this->svgdefs[$attribs['id']] = array('name' => $name, 'attribs' => $attribs);
28206                         return;
28207                 }
28208                 $clipping = false;
28209                 if ($parser == 'clip-path') {
28210                         // set clipping mode
28211                         $clipping = true;
28212                 }
28213                 // get styling properties
28214                 $prev_svgstyle = $this->svgstyles[(count($this->svgstyles) - 1)]; // previous style
28215                 $svgstyle = $this->svgstyles[0]; // set default style
28216                 if (isset($attribs['style']) AND !$this->empty_string($attribs['style'])) {
28217                         // fix style for regular expression
28218                         $attribs['style'] = ';'.$attribs['style'];
28219                 }
28220                 foreach ($prev_svgstyle as $key => $val) {
28221                         if (in_array($key, $this->svginheritprop)) {
28222                                 // inherit previous value
28223                                 $svgstyle[$key] = $val;
28224                         }
28225                         if (isset($attribs[$key]) AND !$this->empty_string($attribs[$key])) {
28226                                 // specific attribute settings
28227                                 if ($attribs[$key] == 'inherit') {
28228                                         $svgstyle[$key] = $val;
28229                                 } else {
28230                                         $svgstyle[$key] = $attribs[$key];
28231                                 }
28232                         } elseif (isset($attribs['style']) AND !$this->empty_string($attribs['style'])) {
28233                                 // CSS style syntax
28234                                 $attrval = array();
28235                                 if (preg_match('/[;\"\s]{1}'.$key.'[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval) AND isset($attrval[1])) {
28236                                         if ($attrval[1] == 'inherit') {
28237                                                 $svgstyle[$key] = $val;
28238                                         } else {
28239                                                 $svgstyle[$key] = $attrval[1];
28240                                         }
28241                                 }
28242                         }
28243                 }
28244                 // transformation matrix
28245                 if (!empty($ctm)) {
28246                         $tm = $ctm;
28247                 } else {
28248                         $tm = $this->svgstyles[(count($this->svgstyles) - 1)]['transfmatrix'];
28249                 }
28250                 if (isset($attribs['transform']) AND !empty($attribs['transform'])) {
28251                         $tm = $this->getTransformationMatrixProduct($tm, $this->getSVGTransformMatrix($attribs['transform']));
28252                 }
28253                 $svgstyle['transfmatrix'] = $tm;
28254                 $invisible = false;
28255                 if (($svgstyle['visibility'] == 'hidden') OR ($svgstyle['visibility'] == 'collapse') OR ($svgstyle['display'] == 'none')) {
28256                         // the current graphics element is invisible (nothing is painted)
28257                         $invisible = true;
28258                 }
28259                 // process tag
28260                 switch($name) {
28261                         case 'defs': {
28262                                 $this->svgdefsmode = true;
28263                                 break;
28264                         }
28265                         // clipPath
28266                         case 'clipPath': {
28267                                 if ($invisible) {
28268                                         break;
28269                                 }
28270                                 $this->svgclipmode = true;
28271                                 if (!isset($attribs['id'])) {
28272                                         $attribs['id'] = 'CP_'.(count($this->svgcliptm) + 1);
28273                                 }
28274                                 $this->svgclipid = $attribs['id'];
28275                                 $this->svgclippaths[$this->svgclipid] = array();
28276                                 $this->svgcliptm[$this->svgclipid] = $tm;
28277                                 break;
28278                         }
28279                         case 'svg': {
28280                                 // start of SVG object
28281                                 break;
28282                         }
28283                         case 'g': {
28284                                 // group together related graphics elements
28285                                 array_push($this->svgstyles, $svgstyle);
28286                                 $this->StartTransform();
28287                                 $this->setSVGStyles($svgstyle, $prev_svgstyle);
28288                                 break;
28289                         }
28290                         case 'linearGradient': {
28291                                 if ($this->pdfa_mode) {
28292                                         break;
28293                                 }
28294                                 if (!isset($attribs['id'])) {
28295                                         $attribs['id'] = 'GR_'.(count($this->svggradients) + 1);
28296                                 }
28297                                 $this->svggradientid = $attribs['id'];
28298                                 $this->svggradients[$this->svggradientid] = array();
28299                                 $this->svggradients[$this->svggradientid]['type'] = 2;
28300                                 $this->svggradients[$this->svggradientid]['stops'] = array();
28301                                 if (isset($attribs['gradientUnits'])) {
28302                                         $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits'];
28303                                 } else {
28304                                         $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox';
28305                                 }
28306                                 //$attribs['spreadMethod']
28307                                 $x1 = (isset($attribs['x1'])?$attribs['x1']:'0%');
28308                                 $y1 = (isset($attribs['y1'])?$attribs['y1']:'0%');
28309                                 $x2 = (isset($attribs['x2'])?$attribs['x2']:'100%');
28310                                 $y2 = (isset($attribs['y2'])?$attribs['y2']:'0%');
28311                                 if (substr($x1, -1) != '%') {
28312                                         $this->svggradients[$this->svggradientid]['mode'] = 'measure';
28313                                 } else {
28314                                         $this->svggradients[$this->svggradientid]['mode'] = 'percentage';
28315                                 }
28316                                 if (isset($attribs['gradientTransform'])) {
28317                                         $this->svggradients[$this->svggradientid]['gradientTransform'] = $this->getSVGTransformMatrix($attribs['gradientTransform']);
28318                                 }
28319                                 $this->svggradients[$this->svggradientid]['coords'] = array($x1, $y1, $x2, $y2);
28320                                 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
28321                                         // gradient is defined on another place
28322                                         $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1);
28323                                 }
28324                                 break;
28325                         }
28326                         case 'radialGradient': {
28327                                 if ($this->pdfa_mode) {
28328                                         break;
28329                                 }
28330                                 if (!isset($attribs['id'])) {
28331                                         $attribs['id'] = 'GR_'.(count($this->svggradients) + 1);
28332                                 }
28333                                 $this->svggradientid = $attribs['id'];
28334                                 $this->svggradients[$this->svggradientid] = array();
28335                                 $this->svggradients[$this->svggradientid]['type'] = 3;
28336                                 $this->svggradients[$this->svggradientid]['stops'] = array();
28337                                 if (isset($attribs['gradientUnits'])) {
28338                                         $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits'];
28339                                 } else {
28340                                         $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox';
28341                                 }
28342                                 //$attribs['spreadMethod']
28343                                 $cx = (isset($attribs['cx'])?$attribs['cx']:0.5);
28344                                 $cy = (isset($attribs['cy'])?$attribs['cy']:0.5);
28345                                 $fx = (isset($attribs['fx'])?$attribs['fx']:$cx);
28346                                 $fy = (isset($attribs['fy'])?$attribs['fy']:$cy);
28347                                 $r = (isset($attribs['r'])?$attribs['r']:0.5);
28348                                 if (isset($attribs['cx']) AND (substr($attribs['cx'], -1) != '%')) {
28349                                         $this->svggradients[$this->svggradientid]['mode'] = 'measure';
28350                                 } else {
28351                                         $this->svggradients[$this->svggradientid]['mode'] = 'percentage';
28352                                 }
28353                                 if (isset($attribs['gradientTransform'])) {
28354                                         $this->svggradients[$this->svggradientid]['gradientTransform'] = $this->getSVGTransformMatrix($attribs['gradientTransform']);
28355                                 }
28356                                 $this->svggradients[$this->svggradientid]['coords'] = array($cx, $cy, $fx, $fy, $r);
28357                                 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
28358                                         // gradient is defined on another place
28359                                         $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1);
28360                                 }
28361                                 break;
28362                         }
28363                         case 'stop': {
28364                                 // gradient stops
28365                                 if (substr($attribs['offset'], -1) == '%') {
28366                                         $offset = floatval(substr($attribs['offset'], -1)) / 100;
28367                                 } else {
28368                                         $offset = floatval($attribs['offset']);
28369                                         if ($offset > 1) {
28370                                                 $offset /= 100;
28371                                         }
28372                                 }
28373                                 $stop_color = isset($svgstyle['stop-color'])?$this->convertHTMLColorToDec($svgstyle['stop-color']):'black';
28374                                 $opacity = isset($svgstyle['stop-opacity'])?$svgstyle['stop-opacity']:1;
28375                                 $this->svggradients[$this->svggradientid]['stops'][] = array('offset' => $offset, 'color' => $stop_color, 'opacity' => $opacity);
28376                                 break;
28377                         }
28378                         // paths
28379                         case 'path': {
28380                                 if ($invisible) {
28381                                         break;
28382                                 }
28383                                 if (isset($attribs['d'])) {
28384                                         $d = trim($attribs['d']);
28385                                         if (!empty($d)) {
28386                                                 if ($clipping) {
28387                                                         $this->SVGTransform($tm);
28388                                                         $this->SVGPath($d, 'CNZ');
28389                                                 } else {
28390                                                         $this->StartTransform();
28391                                                         $this->SVGTransform($tm);
28392                                                         $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, 0, 0, 1, 1, 'SVGPath', array($d, 'CNZ'));
28393                                                         if (!empty($obstyle)) {
28394                                                                 $this->SVGPath($d, $obstyle);
28395                                                         }
28396                                                         $this->StopTransform();
28397                                                 }
28398                                         }
28399                                 }
28400                                 break;
28401                         }
28402                         // shapes
28403                         case 'rect': {
28404                                 if ($invisible) {
28405                                         break;
28406                                 }
28407                                 $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0);
28408                                 $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0);
28409                                 $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0);
28410                                 $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0);
28411                                 $rx = (isset($attribs['rx'])?$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false):0);
28412                                 $ry = (isset($attribs['ry'])?$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false):$rx);
28413                                 if ($clipping) {
28414                                         $this->SVGTransform($tm);
28415                                         $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ', array(), array());
28416                                 } else {
28417                                         $this->StartTransform();
28418                                         $this->SVGTransform($tm);
28419                                         $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'RoundedRectXY', array($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ'));
28420                                         if (!empty($obstyle)) {
28421                                                 $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', $obstyle, array(), array());
28422                                         }
28423                                         $this->StopTransform();
28424                                 }
28425                                 break;
28426                         }
28427                         case 'circle': {
28428                                 if ($invisible) {
28429                                         break;
28430                                 }
28431                                 $cx = (isset($attribs['cx'])?$this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false):0);
28432                                 $cy = (isset($attribs['cy'])?$this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false):0);
28433                                 $r = (isset($attribs['r'])?$this->getHTMLUnitToUnits($attribs['r'], 0, $this->svgunit, false):0);
28434                                 $x = $cx - $r;
28435                                 $y = $cy - $r;
28436                                 $w = 2 * $r;
28437                                 $h = $w;
28438                                 if ($clipping) {
28439                                         $this->SVGTransform($tm);
28440                                         $this->Circle($cx, $cy, $r, 0, 360, 'CNZ', array(), array(), 8);
28441                                 } else {
28442                                         $this->StartTransform();
28443                                         $this->SVGTransform($tm);
28444                                         $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Circle', array($cx, $cy, $r, 0, 360, 'CNZ'));
28445                                         if (!empty($obstyle)) {
28446                                                 $this->Circle($cx, $cy, $r, 0, 360, $obstyle, array(), array(), 8);
28447                                         }
28448                                         $this->StopTransform();
28449                                 }
28450                                 break;
28451                         }
28452                         case 'ellipse': {
28453                                 if ($invisible) {
28454                                         break;
28455                                 }
28456                                 $cx = (isset($attribs['cx'])?$this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false):0);
28457                                 $cy = (isset($attribs['cy'])?$this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false):0);
28458                                 $rx = (isset($attribs['rx'])?$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false):0);
28459                                 $ry = (isset($attribs['ry'])?$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false):0);
28460                                 $x = $cx - $rx;
28461                                 $y = $cy - $ry;
28462                                 $w = 2 * $rx;
28463                                 $h = 2 * $ry;
28464                                 if ($clipping) {
28465                                         $this->SVGTransform($tm);
28466                                         $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ', array(), array(), 8);
28467                                 } else {
28468                                         $this->StartTransform();
28469                                         $this->SVGTransform($tm);
28470                                         $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Ellipse', array($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ'));
28471                                         if (!empty($obstyle)) {
28472                                                 $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, $obstyle, array(), array(), 8);
28473                                         }
28474                                         $this->StopTransform();
28475                                 }
28476                                 break;
28477                         }
28478                         case 'line': {
28479                                 if ($invisible) {
28480                                         break;
28481                                 }
28482                                 $x1 = (isset($attribs['x1'])?$this->getHTMLUnitToUnits($attribs['x1'], 0, $this->svgunit, false):0);
28483                                 $y1 = (isset($attribs['y1'])?$this->getHTMLUnitToUnits($attribs['y1'], 0, $this->svgunit, false):0);
28484                                 $x2 = (isset($attribs['x2'])?$this->getHTMLUnitToUnits($attribs['x2'], 0, $this->svgunit, false):0);
28485                                 $y2 = (isset($attribs['y2'])?$this->getHTMLUnitToUnits($attribs['y2'], 0, $this->svgunit, false):0);
28486                                 $x = $x1;
28487                                 $y = $y1;
28488                                 $w = abs($x2 - $x1);
28489                                 $h = abs($y2 - $y1);
28490                                 if (!$clipping) {
28491                                         $this->StartTransform();
28492                                         $this->SVGTransform($tm);
28493                                         $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Line', array($x1, $y1, $x2, $y2));
28494                                         $this->Line($x1, $y1, $x2, $y2);
28495                                         $this->StopTransform();
28496                                 }
28497                                 break;
28498                         }
28499                         case 'polyline':
28500                         case 'polygon': {
28501                                 if ($invisible) {
28502                                         break;
28503                                 }
28504                                 $points = (isset($attribs['points'])?$attribs['points']:'0 0');
28505                                 $points = trim($points);
28506                                 // note that point may use a complex syntax not covered here
28507                                 $points = preg_split('/[\,\s]+/si', $points);
28508                                 if (count($points) < 4) {
28509                                         break;
28510                                 }
28511                                 $p = array();
28512                                 $xmin = 2147483647;
28513                                 $xmax = 0;
28514                                 $ymin = 2147483647;
28515                                 $ymax = 0;
28516                                 foreach ($points as $key => $val) {
28517                                         $p[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false);
28518                                         if (($key % 2) == 0) {
28519                                                 // X coordinate
28520                                                 $xmin = min($xmin, $p[$key]);
28521                                                 $xmax = max($xmax, $p[$key]);
28522                                         } else {
28523                                                 // Y coordinate
28524                                                 $ymin = min($ymin, $p[$key]);
28525                                                 $ymax = max($ymax, $p[$key]);
28526                                         }
28527                                 }
28528                                 $x = $xmin;
28529                                 $y = $ymin;
28530                                 $w = ($xmax - $xmin);
28531                                 $h = ($ymax - $ymin);
28532                                 if ($name == 'polyline') {
28533                                         $this->StartTransform();
28534                                         $this->SVGTransform($tm);
28535                                         $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'PolyLine', array($p, 'CNZ'));
28536                                         $this->PolyLine($p, 'D', array(), array());
28537                                         $this->StopTransform();
28538                                 } else { // polygon
28539                                         if ($clipping) {
28540                                                 $this->SVGTransform($tm);
28541                                                 $this->Polygon($p, 'CNZ', array(), array(), true);
28542                                         } else {
28543                                                 $this->StartTransform();
28544                                                 $this->SVGTransform($tm);
28545                                                 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Polygon', array($p, 'CNZ'));
28546                                                 if (!empty($obstyle)) {
28547                                                         $this->Polygon($p, $obstyle, array(), array(), true);
28548                                                 }
28549                                                 $this->StopTransform();
28550                                         }
28551                                 }
28552                                 break;
28553                         }
28554                         // image
28555                         case 'image': {
28556                                 if ($invisible) {
28557                                         break;
28558                                 }
28559                                 if (!isset($attribs['xlink:href']) OR empty($attribs['xlink:href'])) {
28560                                         break;
28561                                 }
28562                                 $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0);
28563                                 $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0);
28564                                 $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0);
28565                                 $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0);
28566                                 $img = $attribs['xlink:href'];
28567                                 if (!$clipping) {
28568                                         $this->StartTransform();
28569                                         $this->SVGTransform($tm);
28570                                         $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h);
28571                                         // fix image path
28572                                         if (!$this->empty_string($this->svgdir) AND (($img{0} == '.') OR (basename($img) == $img))) {
28573                                                 // replace relative path with full server path
28574                                                 $img = $this->svgdir.'/'.$img;
28575                                         }
28576                                         if (($img[0] == '/') AND !empty($_SERVER['DOCUMENT_ROOT']) AND ($_SERVER['DOCUMENT_ROOT'] != '/')) {
28577                                                 $findroot = strpos($img, $_SERVER['DOCUMENT_ROOT']);
28578                                                 if (($findroot === false) OR ($findroot > 1)) {
28579                                                         if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') {
28580                                                                 $img = substr($_SERVER['DOCUMENT_ROOT'], 0, -1).$img;
28581                                                         } else {
28582                                                                 $img = $_SERVER['DOCUMENT_ROOT'].$img;
28583                                                         }
28584                                                 }
28585                                         }
28586                                         $img = urldecode($img);
28587                                         $testscrtype = @parse_url($img);
28588                                         if (!isset($testscrtype['query']) OR empty($testscrtype['query'])) {
28589                                                 // convert URL to server path
28590                                                 $img = str_replace(K_PATH_URL, K_PATH_MAIN, $img);
28591                                         }
28592                                         $this->Image($img, $x, $y, $w, $h);
28593                                         $this->StopTransform();
28594                                 }
28595                                 break;
28596                         }
28597                         // text
28598                         case 'text':
28599                         case 'tspan': {
28600                                 $this->svgtextmode['invisible'] = $invisible;
28601                                 if ($invisible) {
28602                                         break;
28603                                 }
28604                                 array_push($this->svgstyles, $svgstyle);
28605                                 // only basic support - advanced features must be implemented
28606                                 $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):$this->x);
28607                                 $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):$this->y);
28608                                 $svgstyle['text-color'] = $svgstyle['fill'];
28609                                 $this->svgtext = '';
28610                                 if (isset($svgstyle['text-anchor'])) {
28611                                         $this->svgtextmode['text-anchor'] = $svgstyle['text-anchor'];
28612                                 } else {
28613                                         $this->svgtextmode['text-anchor'] = 'start';
28614                                 }
28615                                 if (isset($svgstyle['direction'])) {
28616                                         if ($svgstyle['direction'] == 'rtl') {
28617                                                 $this->svgtextmode['rtl'] = true;
28618                                         } else {
28619                                                 $this->svgtextmode['rtl'] = false;
28620                                         }
28621                                 } else {
28622                                         $this->svgtextmode['rtl'] = false;
28623                                 }
28624                                 if (isset($svgstyle['stroke']) AND ($svgstyle['stroke'] != 'none') AND isset($svgstyle['stroke-width']) AND ($svgstyle['stroke-width'] > 0)) {
28625                                         $this->svgtextmode['stroke'] = $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false);
28626                                 } else {
28627                                         $this->svgtextmode['stroke'] = false;
28628                                 }
28629                                 $this->StartTransform();
28630                                 $this->SVGTransform($tm);
28631                                 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, 1, 1);
28632                                 $this->x = $x;
28633                                 $this->y = $y;
28634                                 break;
28635                         }
28636                         // use
28637                         case 'use': {
28638                                 if (isset($attribs['xlink:href'])) {
28639                                         $use = $this->svgdefs[substr($attribs['xlink:href'], 1)];
28640                                         if (isset($attribs['xlink:href'])) {
28641                                                 unset($attribs['xlink:href']);
28642                                         }
28643                                         if (isset($attribs['id'])) {
28644                                                 unset($attribs['id']);
28645                                         }
28646                                         $attribs = array_merge($use['attribs'], $attribs);
28647                                         $this->startSVGElementHandler($parser, $use['name'], $use['attribs']);
28648                                 }
28649                                 break;
28650                         }
28651                         default: {
28652                                 break;
28653                         }
28654                 } // end of switch
28655         }
28656 
28665         protected function endSVGElementHandler($parser, $name) {
28666                 switch($name) {
28667                         case 'defs': {
28668                                 $this->svgdefsmode = false;
28669                                 break;
28670                         }
28671                         // clipPath
28672                         case 'clipPath': {
28673                                 $this->svgclipmode = false;
28674                                 break;
28675                         }
28676                         case 'g': {
28677                                 // ungroup: remove last style from array
28678                                 array_pop($this->svgstyles);
28679                                 $this->StopTransform();
28680                                 break;
28681                         }
28682                         case 'text':
28683                         case 'tspan': {
28684                                 if ($this->svgtextmode['invisible']) {
28685                                         // This implementation must be fixed to following the rule:
28686                                         // 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.
28687                                         break;
28688                                 }
28689                                 // print text
28690                                 $text = $this->stringTrim($this->svgtext);
28691                                 if ($this->svgtextmode['text-anchor'] != 'start') {
28692                                         $textlen = $this->GetStringWidth($text);
28693                                         // check if string is RTL text
28694                                         if ($this->svgtextmode['text-anchor'] == 'end') {
28695                                                 if ($this->svgtextmode['rtl']) {
28696                                                         $this->x += $textlen;
28697                                                 } else {
28698                                                         $this->x -= $textlen;
28699                                                 }
28700                                         } elseif ($this->svgtextmode['text-anchor'] == 'middle') {
28701                                                 if ($this->svgtextmode['rtl']) {
28702                                                         $this->x += ($textlen / 2);
28703                                                 } else {
28704                                                         $this->x -= ($textlen / 2);
28705                                                 }
28706                                         }
28707                                 }
28708                                 $textrendermode = $this->textrendermode;
28709                                 $textstrokewidth = $this->textstrokewidth;
28710                                 $this->setTextRenderingMode($this->svgtextmode['stroke'], true, false);
28711                                 $this->Cell(0, 0, $text, 0, 0, '', false, '', 0, false, 'L', 'T');
28712                                 // restore previous rendering mode
28713                                 $this->textrendermode = $textrendermode;
28714                                 $this->textstrokewidth = $textstrokewidth;
28715                                 $this->svgtext = '';
28716                                 $this->StopTransform();
28717                                 array_pop($this->svgstyles);
28718                                 break;
28719                         }
28720                         default: {
28721                                 break;
28722                         }
28723                 }
28724         }
28725 
28734         protected function segSVGContentHandler($parser, $data) {
28735                 $this->svgtext .= $data;
28736         }
28737 
28738         // --- END SVG METHODS -----------------------------------------------------
28739 
28740 } // END OF TCPDF CLASS
28741 
28742 //============================================================+
28743 // END OF FILE
28744 //============================================================+
 All Data Structures Namespaces Files Functions Variables Enumerations