tcpdf.php

Go to the documentation of this file.
00001 <?php
00002 //============================================================+
00003 // File name   : tcpdf.php
00004 // Begin       : 2002-08-03
00005 // Last Update : 2009-05-07
00006 // Author      : Nicola Asuni - [email protected] - http://www.tcpdf.org
00007 // Version     : 4.6.008
00008 // License     : GNU LGPL (http://www.gnu.org/copyleft/lesser.html)
00009 //  ----------------------------------------------------------------------------
00010 //  Copyright (C) 2002-2009  Nicola Asuni - Tecnick.com S.r.l.
00011 //  
00012 //  This program is free software: you can redistribute it and/or modify
00013 //  it under the terms of the GNU Lesser General Public License as published by
00014 //  the Free Software Foundation, either version 2.1 of the License, or
00015 //  (at your option) any later version.
00016 //  
00017 //  This program is distributed in the hope that it will be useful,
00018 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00019 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020 //  GNU Lesser General Public License for more details.
00021 //  
00022 //  You should have received a copy of the GNU Lesser General Public License
00023 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
00024 //  
00025 //  See LICENSE.TXT file for more information.
00026 //  ----------------------------------------------------------------------------
00027 //
00028 // Description : This is a PHP class for generating PDF documents without 
00029 //               requiring external extensions.
00030 //
00031 // NOTE:
00032 // This class was originally derived in 2002 from the Public 
00033 // Domain FPDF class by Olivier Plathey (http://www.fpdf.org), 
00034 // but now is almost entirely rewritten.
00035 //
00036 // Main features:
00037 //  * no external libraries are required for the basic functions;
00038 //  * supports all ISO page formats;
00039 //  * supports custom page formats, margins and units of measure;
00040 //  * supports UTF-8 Unicode and Right-To-Left languages;
00041 //  * supports TrueTypeUnicode, OpenTypeUnicode, TrueType, OpenType, Type1 and CID-0 fonts;
00042 //  * supports document encryption;
00043 //  * includes methods to publish some XHTML code;
00044 //  * includes graphic (geometric) and transformation methods;
00045 //  * includes Javascript and forms support;
00046 //  * includes a method to print various barcode formats: 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;
00047 //  * includes methods to set Bookmarks and print a Table of Content;
00048 //  * includes methods to move and delete pages;
00049 //  * includes methods for automatic page header and footer management;
00050 //  * supports automatic page break;
00051 //  * supports automatic page numbering and page groups;
00052 //  * supports automatic line break and text justification;
00053 //  * supports JPEG and PNG 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)
00054 //  * supports stroke and clipping mode for text;
00055 //  * supports clipping masks;
00056 //  * supports Grayscale, RGB, CMYK, Spot Colors and Transparencies;
00057 //  * supports several annotations, including links, text and file attachments;
00058 //  * supports page compression (requires zlib extension);
00059 //  * supports text hyphenation.
00060 //  * supports transactions to UNDO commands.
00061 //
00062 // -----------------------------------------------------------
00063 // THANKS TO:
00064 // 
00065 // Olivier Plathey (http://www.fpdf.org) for original FPDF.
00066 // Efthimios Mavrogeorgiadis ([email protected]) for suggestions on RTL language support.
00067 // Klemen Vodopivec (http://www.fpdf.de/downloads/addons/37/) for Encryption algorithm.
00068 // Warren Sherliker ([email protected]) for better image handling.
00069 // dullus for text Justification.
00070 // Bob Vincent ([email protected]) for <li> value attribute.
00071 // Patrick Benny for text stretch suggestion on Cell().
00072 // Johannes Güntert for JavaScript support.
00073 // Denis Van Nuffelen for Dynamic Form.
00074 // Jacek Czekaj for multibyte justification
00075 // Anthony Ferrara for the reintroduction of legacy image methods.
00076 // Sourceforge user 1707880 (hucste) for line-trough mode.
00077 // Larry Stanbery for page groups.
00078 // Martin Hall-May for transparency.
00079 // Aaron C. Spike for Polycurve method.
00080 // Mohamad Ali Golkar, Saleh AlMatrafe, Charles Abbott for Arabic and Persian support.
00081 // Moritz Wagner and Andreas Wurmser for graphic functions.
00082 // Andrew Whitehead for core fonts support.
00083 // Esteban Joël Marín for OpenType font conversion.
00084 // Teus Hagen for several suggestions and fixes.
00085 // Yukihiro Nakadaira for CID-0 CJK fonts fixes.
00086 // Kosmas Papachristos for some CSS improvements.
00087 // Marcel Partap for some fixes.
00088 // Won Kyu Park for several suggestions, fixes and patches.
00089 // Anyone that has reported a bug or sent a suggestion.
00090 //============================================================+
00091 
00134 require_once(dirname(__FILE__).'/config/tcpdf_config.php');
00135 
00136 // includes some support files
00137 
00141 require_once(dirname(__FILE__).'/unicode_data.php');
00142 
00146 require_once(dirname(__FILE__).'/htmlcolors.php');
00147 
00148 if (!class_exists('TCPDF', false)) {
00152     define('PDF_PRODUCER', 'TCPDF 4.6.008 (http://www.tcpdf.org)');
00153     
00163     class TCPDF {
00164         
00165         // protected or 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 
00291         //protected
00292         public $cMargin;
00293         
00298         protected $oldcMargin;
00299 
00304         protected $x;
00305 
00310         protected $y;
00311 
00316         protected $lasth;
00317 
00322         protected $LineWidth;
00323 
00328         protected $CoreFonts;
00329 
00334         protected $fonts = array();
00335 
00340         protected $FontFiles = array();
00341 
00346         protected $diffs = array();
00347 
00352         protected $images = array();
00353 
00358         protected $PageAnnots = array();
00359 
00364         protected $links = array();
00365 
00370         protected $FontFamily;
00371 
00376         protected $FontStyle;
00377         
00383         protected $FontAscent;
00384         
00390         protected $FontDescent;
00391 
00396         protected $underline;
00397 
00402         protected $CurrentFont;
00403 
00408         protected $FontSizePt;
00409 
00414         protected $FontSize;
00415 
00420         protected $DrawColor;
00421 
00426         protected $FillColor;
00427 
00432         protected $TextColor;
00433 
00438         protected $ColorFlag;
00439 
00444         protected $AutoPageBreak;
00445 
00450         protected $PageBreakTrigger;
00451 
00456         protected $InFooter = false;
00457 
00462         protected $ZoomMode;
00463 
00468         protected $LayoutMode;
00469 
00474         protected $title = '';
00475 
00480         protected $subject = '';
00481 
00486         protected $author = '';
00487 
00492         protected $keywords = '';
00493 
00498         protected $creator = '';
00499 
00504         protected $AliasNbPages = '{nb}';
00505         
00510         protected $AliasNumPage = '{pnb}';
00511         
00518         protected $img_rb_x;
00519 
00526         protected $img_rb_y;
00527 
00534         protected $imgscale = 1;
00535 
00542         protected $isunicode = false;
00543 
00549         protected $PDFVersion = '1.7';
00550         
00551         
00552         // ----------------------
00553         
00558         protected $header_margin;
00559         
00564         protected $footer_margin;
00565         
00571         protected $original_lMargin;
00572         
00578         protected $original_rMargin;
00579             
00584         protected $header_font;
00585         
00590         protected $footer_font;
00591         
00596         protected $l;
00597         
00602         protected $barcode = false;
00603         
00608         protected $print_header = true;
00609         
00614         protected $print_footer = true;
00615             
00620         protected $header_logo = '';
00621         
00626         protected $header_logo_width = 30;
00627         
00632         protected $header_title = '';
00633         
00638         protected $header_string = '';
00639         
00644         protected $default_table_columns = 4;
00645         
00646         
00647         // variables for html parser
00648         
00653         protected $HREF = array();
00654         
00659         protected $fontlist = array();
00660         
00665         protected $fgcolor;
00666                         
00671         protected $listordered = array();
00672         
00677         protected $listcount = array();
00678         
00683         protected $listnum = 0;
00684         
00689         protected $listindent;
00690         
00695         protected $bgcolor;
00696         
00701         protected $tempfontsize = 10;
00702         
00707         protected $lispacer = '';
00708         
00714         protected $encoding = 'UTF-8';
00715         
00721         protected $internal_encoding;
00722         
00728         protected $rtl = false;
00729         
00735         protected $tmprtl = false;
00736         
00737         // --- Variables used for document encryption:
00738         
00744         protected $encrypted;
00745         
00751         protected $Uvalue;
00752         
00758         protected $Ovalue;
00759         
00765         protected $Pvalue;
00766         
00772         protected $enc_obj_id;
00773         
00779         protected $last_rc4_key;
00780         
00786         protected $last_rc4_key_c;
00787         
00792         protected $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";
00793         
00798         protected $encryption_key;
00799         
00800         // --- bookmark ---
00801         
00807         protected $outlines = array();
00808         
00814         protected $OutlineRoot;
00815         
00816         
00817         // --- javascript and form ---
00818         
00824         protected $javascript = '';
00825         
00831         protected $n_js;
00832 
00838         protected $linethrough;
00839 
00840         // --- Variables used for User's Rights ---
00841         // See PDF reference chapter 8.7 Digital Signatures
00842 
00848         protected $ur;
00849 
00855         protected $ur_document;
00856 
00862         protected $ur_annots;
00863 
00869         protected $ur_form;
00870 
00876         protected $ur_signature;
00877 
00883         protected $dpi = 72;
00884         
00890         protected $newpagegroup = array();
00891         
00897         protected $pagegroups;
00898         
00904         protected $currpagegroup; 
00905         
00911         protected $visibility = 'all';
00912         
00918         protected $n_ocg_print;
00919         
00925         protected $n_ocg_view;
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         
00975         protected $intmrk = array();
00976         
00982         protected $footerpos = array();
00983         
00984         
00990         protected $footerlen = array();
00991         
00997         protected $newline = true;
00998         
01004         protected $endlinex = 0;
01005         
01011         protected $linestyleWidth = '';
01012         
01018         protected $linestyleCap = '0 J';
01019         
01025         protected $linestyleJoin = '0 j';
01026         
01032         protected $linestyleDash = '[] 0 d';
01033         
01039         protected $openMarkedContent = false;
01040         
01046         protected $htmlvspace = 0;
01047         
01053         protected $spot_colors = array();
01054         
01060         protected $lisymbol = '';
01061         
01067         protected $epsmarker = 'x#!#EPS#!#x';
01068         
01074         protected $transfmatrix = array();
01075         
01081         protected $booklet = false;
01082         
01088         protected $feps = 0.001;
01089         
01095         protected $tagvspaces = array();
01096         
01103         protected $customlistindent = -1;
01104         
01110         protected $opencell = true;
01111 
01117         protected $embeddedfiles = array();
01118 
01124         protected $premode = false;
01125 
01132         protected $transfmrk = array();
01133 
01139         protected $htmlLinkColorArray = array(0, 0, 255);
01140 
01146         protected $htmlLinkFontStyle = 'U';
01147 
01153         protected $numpages = 0;
01154 
01160         protected $pagelen = array();
01161 
01167         protected $numimages = 0;
01168 
01174         protected $imagekeys = array();
01175 
01181         protected $bufferlen = 0;
01182 
01188         protected $diskcache = false;
01189 
01195         protected $numfonts = 0;
01196 
01202         protected $fontkeys = array();
01203 
01209         protected $pageopen = array();
01210         
01216         protected $default_monospaced_font = 'courier';
01217 
01223         protected $objcopy;
01224 
01230         protected $cache_file_lenght = array();
01231 
01237         protected $thead = '';
01238 
01244         protected $theadMargin = '';
01245 
01251         protected $cache_UTF8StringToArray = array();
01252 
01258         protected $cache_maxsize_UTF8StringToArray = 8;
01259 
01265         protected $cache_size_UTF8StringToArray = 0;
01266 
01272         protected $sign = false;
01273 
01279         protected $signature_data = array();
01280 
01286         protected $signature_max_lenght = 5120;
01287 
01293         protected $re_spaces = '/[\s\p{Z}\p{Lo}]/';
01294 
01295         //------------------------------------------------------------
01296         // METHODS
01297         //------------------------------------------------------------
01298 
01312         public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false) {
01313             /* Set internal character encoding to ASCII */
01314             if (function_exists('mb_internal_encoding') AND mb_internal_encoding()) {
01315                 $this->internal_encoding = mb_internal_encoding();
01316                 mb_internal_encoding('ASCII');
01317             }
01318             // set disk caching
01319             $this->diskcache = $diskcache ? true : false;
01320             // set language direction
01321             $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
01322             $this->tmprtl = false;
01323             //Some checks
01324             $this->_dochecks();
01325             //Initialization of properties
01326             $this->isunicode = $unicode;
01327             $this->page = 0;
01328             $this->transfmrk[0] = array();
01329             $this->pagedim = array();
01330             $this->n = 2;
01331             $this->buffer = '';
01332             $this->pages = array();
01333             $this->state = 0;
01334             $this->fonts = array();
01335             $this->FontFiles = array();
01336             $this->diffs = array();
01337             $this->images = array();
01338             $this->links = array();
01339             $this->gradients = array();
01340             $this->InFooter = false;
01341             $this->lasth = 0;
01342             $this->FontFamily = 'helvetica';
01343             $this->FontStyle = '';
01344             $this->FontSizePt = 12;
01345             $this->underline = false;
01346             $this->linethrough = false;
01347             $this->DrawColor = '0 G';
01348             $this->FillColor = '0 g';
01349             $this->TextColor = '0 g';
01350             $this->ColorFlag = false;
01351             // encryption values
01352             $this->encrypted = false;
01353             $this->last_rc4_key = '';
01354             $this->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";
01355             //Standard Unicode fonts
01356             $this->CoreFonts = array(
01357                 'courier'=>'Courier',
01358                 'courierB'=>'Courier-Bold',
01359                 'courierI'=>'Courier-Oblique',
01360                 'courierBI'=>'Courier-BoldOblique',
01361                 'helvetica'=>'Helvetica',
01362                 'helveticaB'=>'Helvetica-Bold',
01363                 'helveticaI'=>'Helvetica-Oblique',
01364                 'helveticaBI'=>'Helvetica-BoldOblique',
01365                 'times'=>'Times-Roman',
01366                 'timesB'=>'Times-Bold',
01367                 'timesI'=>'Times-Italic',
01368                 'timesBI'=>'Times-BoldItalic',
01369                 'symbol'=>'Symbol',
01370                 'zapfdingbats'=>'ZapfDingbats'
01371             );
01372             //Set scale factor
01373             $this->setPageUnit($unit);
01374             // set page format and orientation
01375             $this->setPageFormat($format, $orientation);
01376             //Page margins (1 cm)
01377             $margin = 28.35 / $this->k;
01378             $this->SetMargins($margin, $margin);
01379             //Interior cell margin
01380             $this->cMargin = $margin / 10;
01381             //Line width (0.2 mm)
01382             $this->LineWidth = 0.57 / $this->k;
01383             $this->linestyleWidth = sprintf('%.2F w', ($this->LineWidth * $this->k));
01384             $this->linestyleCap = '0 J';
01385             $this->linestyleJoin = '0 j';
01386             $this->linestyleDash = '[] 0 d';
01387             //Automatic page break
01388             $this->SetAutoPageBreak(true, (2 * $margin));
01389             //Full width display mode
01390             $this->SetDisplayMode('fullwidth');
01391             //Compression
01392             $this->SetCompression(true);
01393             //Set default PDF version number
01394             $this->PDFVersion = '1.7';
01395             $this->encoding = $encoding;
01396             $this->HREF = array();
01397             $this->getFontsList();
01398             $this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0);
01399             $this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255);
01400             $this->extgstates = array();
01401             // user's rights
01402             $this->sign = false;
01403             $this->ur = false;
01404             $this->ur_document = '/FullSave';
01405             $this->ur_annots = '/Create/Delete/Modify/Copy/Import/Export';
01406             $this->ur_form = '/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate';
01407             $this->ur_signature = '/Modify';            
01408             // set default JPEG quality
01409             $this->jpeg_quality = 75;
01410             // initialize some settings
01411             $this->utf8Bidi(array(''), '');
01412             // set default font
01413             $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
01414             // check if PCRE Unicode support is enabled
01415             if (@preg_match('/\pL/u', 'a') == 1) {
01416                 // PCRE unicode support is turned ON
01417                 // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
01418                 // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
01419                 // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
01420                 $this->re_spaces = '/[\s\p{Z}\p{Lo}]/';
01421             } else {
01422                 // PCRE unicode support is turned OFF
01423                 $this->re_spaces = '/[\s]/';
01424             }
01425         }
01426         
01432         public function __destruct() {
01433             // restore internal encoding
01434             if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) {
01435                 mb_internal_encoding($this->internal_encoding);
01436             }
01437             // unset all class variables
01438             $this->_destroy(true);
01439         }
01440         
01447         public function setPageUnit($unit) {
01448         //Set scale factor
01449             switch (strtolower($unit)) {
01450                 // points
01451                 case 'px':
01452                 case 'pt': {
01453                     $this->k = 1;
01454                     break;
01455                 }
01456                 // millimeters
01457                 case 'mm': {
01458                     $this->k = $this->dpi / 25.4;
01459                     break;
01460                 }
01461                 // centimeters
01462                 case 'cm': {
01463                     $this->k = $this->dpi / 2.54;
01464                     break;
01465                 }
01466                 // inches
01467                 case 'in': {
01468                     $this->k = $this->dpi;
01469                     break;
01470                 }
01471                 // unsupported unit
01472                 default : {
01473                     $this->Error('Incorrect unit: '.$unit);
01474                     break;
01475                 }
01476             }
01477             if (isset($this->CurOrientation)) {
01478                     $this->setPageOrientation($this->CurOrientation);
01479             }
01480         }
01481         
01489         public function setPageFormat($format, $orientation='P') {
01490             //Page format
01491             if (is_string($format)) {
01492                 // Page formats (45 standard ISO paper formats and 4 american common formats).
01493                 // Paper cordinates are calculated in this way: (inches * 72) where (1 inch = 2.54 cm)
01494                 switch (strtoupper($format)) {
01495                     case '4A0': {$format = array(4767.87,6740.79); break;}
01496                     case '2A0': {$format = array(3370.39,4767.87); break;}
01497                     case 'A0': {$format = array(2383.94,3370.39); break;}
01498                     case 'A1': {$format = array(1683.78,2383.94); break;}
01499                     case 'A2': {$format = array(1190.55,1683.78); break;}
01500                     case 'A3': {$format = array(841.89,1190.55); break;}
01501                     case 'A4': default: {$format = array(595.28,841.89); break;}
01502                     case 'A5': {$format = array(419.53,595.28); break;}
01503                     case 'A6': {$format = array(297.64,419.53); break;}
01504                     case 'A7': {$format = array(209.76,297.64); break;}
01505                     case 'A8': {$format = array(147.40,209.76); break;}
01506                     case 'A9': {$format = array(104.88,147.40); break;}
01507                     case 'A10': {$format = array(73.70,104.88); break;}
01508                     case 'B0': {$format = array(2834.65,4008.19); break;}
01509                     case 'B1': {$format = array(2004.09,2834.65); break;}
01510                     case 'B2': {$format = array(1417.32,2004.09); break;}
01511                     case 'B3': {$format = array(1000.63,1417.32); break;}
01512                     case 'B4': {$format = array(708.66,1000.63); break;}
01513                     case 'B5': {$format = array(498.90,708.66); break;}
01514                     case 'B6': {$format = array(354.33,498.90); break;}
01515                     case 'B7': {$format = array(249.45,354.33); break;}
01516                     case 'B8': {$format = array(175.75,249.45); break;}
01517                     case 'B9': {$format = array(124.72,175.75); break;}
01518                     case 'B10': {$format = array(87.87,124.72); break;}
01519                     case 'C0': {$format = array(2599.37,3676.54); break;}
01520                     case 'C1': {$format = array(1836.85,2599.37); break;}
01521                     case 'C2': {$format = array(1298.27,1836.85); break;}
01522                     case 'C3': {$format = array(918.43,1298.27); break;}
01523                     case 'C4': {$format = array(649.13,918.43); break;}
01524                     case 'C5': {$format = array(459.21,649.13); break;}
01525                     case 'C6': {$format = array(323.15,459.21); break;}
01526                     case 'C7': {$format = array(229.61,323.15); break;}
01527                     case 'C8': {$format = array(161.57,229.61); break;}
01528                     case 'C9': {$format = array(113.39,161.57); break;}
01529                     case 'C10': {$format = array(79.37,113.39); break;}
01530                     case 'RA0': {$format = array(2437.80,3458.27); break;}
01531                     case 'RA1': {$format = array(1729.13,2437.80); break;}
01532                     case 'RA2': {$format = array(1218.90,1729.13); break;}
01533                     case 'RA3': {$format = array(864.57,1218.90); break;}
01534                     case 'RA4': {$format = array(609.45,864.57); break;}
01535                     case 'SRA0': {$format = array(2551.18,3628.35); break;}
01536                     case 'SRA1': {$format = array(1814.17,2551.18); break;}
01537                     case 'SRA2': {$format = array(1275.59,1814.17); break;}
01538                     case 'SRA3': {$format = array(907.09,1275.59); break;}
01539                     case 'SRA4': {$format = array(637.80,907.09); break;}
01540                     case 'LETTER': {$format = array(612.00,792.00); break;}
01541                     case 'LEGAL': {$format = array(612.00,1008.00); break;}
01542                     case 'EXECUTIVE': {$format = array(521.86,756.00); break;}
01543                     case 'FOLIO': {$format = array(612.00,936.00); break;}
01544                 }
01545                 $this->fwPt = $format[0];
01546                 $this->fhPt = $format[1];
01547             } else {
01548                 $this->fwPt = $format[0] * $this->k;
01549                 $this->fhPt = $format[1] * $this->k;
01550             }
01551             $this->setPageOrientation($orientation);
01552         }
01553         
01562         public function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') {
01563             $orientation = strtoupper($orientation);
01564             if (($orientation == 'P') OR ($orientation == 'PORTRAIT')) {
01565                 $this->CurOrientation = 'P';
01566                 $this->wPt = $this->fwPt;
01567                 $this->hPt = $this->fhPt;
01568             } elseif (($orientation == 'L') OR ($orientation == 'LANDSCAPE')) {
01569                 $this->CurOrientation = 'L';
01570                 $this->wPt = $this->fhPt;
01571                 $this->hPt = $this->fwPt;
01572             } else {
01573                 $this->Error('Incorrect orientation: '.$orientation);
01574             }
01575             $this->w = $this->wPt / $this->k;
01576             $this->h = $this->hPt / $this->k;
01577             if ($this->empty_string($autopagebreak)) {
01578                 if (isset($this->AutoPageBreak)) {
01579                     $autopagebreak = $this->AutoPageBreak;
01580                 } else {
01581                     $autopagebreak = true;
01582                 }
01583             }
01584             if ($this->empty_string($bottommargin)) {
01585                 if (isset($this->bMargin)) {
01586                     $bottommargin = $this->bMargin;
01587                 } else {
01588                     // default value = 2 cm
01589                     $bottommargin = 2 * 28.35 / $this->k;
01590                 }
01591             }
01592             $this->SetAutoPageBreak($autopagebreak, $bottommargin);
01593             // store page dimensions
01594             $this->pagedim[$this->page] = array('w' => $this->wPt, 'h' => $this->hPt, 'wk' => $this->w, 'hk' => $this->h, 'tm' => $this->tMargin, 'bm' => $bottommargin, 'lm' => $this->lMargin, 'rm' => $this->rMargin, 'pb' => $autopagebreak, 'or' => $this->CurOrientation, 'olm' => $this->original_lMargin, 'orm' => $this->original_rMargin);
01595         }
01596                 
01603         public function setRTL($enable) {
01604             $this->rtl = $enable ? true : false;
01605             $this->tmprtl = false;
01606         }
01607         
01614         public function getRTL() {
01615             return $this->rtl;
01616         }
01617         
01624         public function setTempRTL($mode) {
01625             switch ($mode) {
01626                 case false:
01627                 case 'L':
01628                 case 'R': {
01629                     $this->tmprtl = $mode;
01630                 }
01631             }
01632         }
01633         
01641         public function setLastH($h) {
01642             $this->lasth = $h;
01643         }
01644         
01651         public function getLastH() {
01652             return $this->lasth;
01653         }
01654         
01662         public function setImageScale($scale) {
01663             $this->imgscale = $scale;
01664         }
01665 
01673         public function getImageScale() {
01674             return $this->imgscale;
01675         }
01676                 
01686         public function getPageDimensions($pagenum='') {
01687             if (empty($pagenum)) {
01688                 $pagenum = $this->page;
01689             }
01690             return $this->pagedim[$pagenum];
01691         }
01692         
01702         public function getPageWidth($pagenum='') {
01703             if (empty($pagenum)) {
01704                 return $this->w;
01705             }
01706             return $this->pagedim[$pagenum]['w'];
01707         }
01708 
01718         public function getPageHeight($pagenum='') {
01719             if (empty($pagenum)) {
01720                 return $this->h;
01721             }
01722             return $this->pagedim[$pagenum]['h'];
01723         }
01724 
01734         public function getBreakMargin($pagenum='') {
01735             if (empty($pagenum)) {
01736                 return $this->bMargin;
01737             }
01738             return $this->pagedim[$pagenum]['bm'];
01739         }
01740 
01748         public function getScaleFactor() {
01749             return $this->k;
01750         }
01751 
01761         public function SetMargins($left, $top, $right=-1) {
01762             //Set left, top and right margins
01763             $this->lMargin = $left;
01764             $this->tMargin = $top;
01765             if ($right == -1) {
01766                 $right = $left;
01767             }
01768             $this->rMargin = $right;
01769         }
01770 
01778         public function SetLeftMargin($margin) {
01779             //Set left margin
01780             $this->lMargin=$margin;
01781             if (($this->page > 0) AND ($this->x < $margin)) {
01782                 $this->x = $margin;
01783             }
01784         }
01785 
01793         public function SetTopMargin($margin) {
01794             //Set top margin
01795             $this->tMargin=$margin;
01796             if (($this->page > 0) AND ($this->y < $margin)) {
01797                 $this->y = $margin;
01798             }
01799         }
01800 
01808         public function SetRightMargin($margin) {
01809             $this->rMargin=$margin;
01810             if (($this->page > 0) AND ($this->x > ($this->w - $margin))) {
01811                 $this->x = $this->w - $margin;
01812             }
01813         }
01814 
01822         public function SetCellPadding($pad) {
01823             $this->cMargin = $pad;
01824         }
01825 
01834         public function SetAutoPageBreak($auto, $margin=0) {
01835             //Set auto page break mode and triggering margin
01836             $this->AutoPageBreak = $auto;
01837             $this->bMargin = $margin;
01838             $this->PageBreakTrigger = $this->h - $margin;
01839         }
01840 
01849         public function SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone') {
01850             //Set display mode in viewer
01851             if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) {
01852                 $this->ZoomMode = $zoom;
01853             } else {
01854                 $this->Error('Incorrect zoom display mode: '.$zoom);
01855             }
01856             switch ($layout) {
01857                 case 'default':
01858                 case 'single':
01859                 case 'SinglePage': {
01860                     $this->LayoutMode = 'SinglePage';
01861                     break;
01862                 }
01863                 case 'continuous':
01864                 case 'OneColumn': {
01865                     $this->LayoutMode = 'OneColumn';
01866                     break;
01867                 }
01868                 case 'two':
01869                 case 'TwoColumnLeft': {
01870                     $this->LayoutMode = 'TwoColumnLeft';
01871                     break;
01872                 }
01873                 case 'TwoColumnRight': {
01874                     $this->LayoutMode = 'TwoColumnRight';
01875                     break;
01876                 }
01877                 case 'TwoPageLeft': {
01878                     $this->LayoutMode = 'TwoPageLeft';
01879                     break;
01880                 }
01881                 case 'TwoPageRight': {
01882                     $this->LayoutMode = 'TwoPageRight';
01883                     break;
01884                 }
01885                 default: {
01886                     $this->LayoutMode = 'SinglePage';
01887                 }
01888             }
01889             switch ($mode) {
01890                 case 'UseNone': {
01891                     $this->PageMode = 'UseNone';
01892                     break;
01893                 }
01894                 case 'UseOutlines': {
01895                     $this->PageMode = 'UseOutlines';
01896                     break;
01897                 }
01898                 case 'UseThumbs': {
01899                     $this->PageMode = 'UseThumbs';
01900                     break;
01901                 }
01902                 case 'FullScreen': {
01903                     $this->PageMode = 'FullScreen';
01904                     break;
01905                 }
01906                 case 'UseOC': {
01907                     $this->PageMode = 'UseOC';
01908                     break;
01909                 }
01910                 case '': {
01911                     $this->PageMode = 'UseAttachments';
01912                     break;
01913                 }
01914                 default: {
01915                     $this->PageMode = 'UseNone';
01916                 }
01917             }
01918         }
01919 
01927         public function SetCompression($compress) {
01928             //Set page compression
01929             if (function_exists('gzcompress')) {
01930                 $this->compress = $compress;
01931             } else {
01932                 $this->compress = false;
01933             }
01934         }
01935 
01943         public function SetTitle($title) {
01944             //Title of document
01945             $this->title = $title;
01946         }
01947 
01955         public function SetSubject($subject) {
01956             //Subject of document
01957             $this->subject = $subject;
01958         }
01959 
01967         public function SetAuthor($author) {
01968             //Author of document
01969             $this->author = $author;
01970         }
01971 
01979         public function SetKeywords($keywords) {
01980             //Keywords of document
01981             $this->keywords = $keywords;
01982         }
01983 
01991         public function SetCreator($creator) {
01992             //Creator of document
01993             $this->creator = $creator;
01994         }
01995         
02003         public function Error($msg) {
02004             // unset all class variables
02005             $this->_destroy(true);
02006             // exit program and print error
02007             die('<strong>TCPDF ERROR: </strong>'.$msg);
02008         }
02009 
02018         public function Open() {
02019             //Begin document
02020             $this->state = 1;
02021         }
02022 
02031         public function Close() {
02032             if ($this->state == 3) {
02033                 return;
02034             }
02035             if ($this->page == 0) {
02036                 $this->AddPage();
02037             }
02038             // close page
02039             $this->endPage();
02040             // close document
02041             $this->_enddoc();
02042             // unset all class variables (except critical ones)
02043             $this->_destroy(false);
02044         }
02045         
02054         public function setPage($pnum, $resetmargins=false) {
02055             if ($pnum == $this->page) {
02056                 return;
02057             }
02058             if (($pnum > 0) AND ($pnum <= $this->numpages)) {
02059                 $this->state = 2;
02060                 // save current graphic settings
02061                 //$gvars = $this->getGraphicVars();
02062                 $oldpage = $this->page;
02063                 $this->page = $pnum;
02064                 $this->wPt = $this->pagedim[$this->page]['w'];
02065                 $this->hPt = $this->pagedim[$this->page]['h'];
02066                 $this->w = $this->wPt / $this->k;
02067                 $this->h = $this->hPt / $this->k;
02068                 $this->tMargin = $this->pagedim[$this->page]['tm'];
02069                 $this->bMargin = $this->pagedim[$this->page]['bm'];
02070                 $this->original_lMargin = $this->pagedim[$this->page]['olm'];
02071                 $this->original_rMargin = $this->pagedim[$this->page]['orm'];
02072                 $this->AutoPageBreak = $this->pagedim[$this->page]['pb'];
02073                 $this->CurOrientation = $this->pagedim[$this->page]['or'];
02074                 $this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin);
02075                 // restore graphic settings
02076                 //$this->setGraphicVars($gvars);
02077                 if ($resetmargins) {
02078                     $this->lMargin = $this->pagedim[$this->page]['olm'];
02079                     $this->rMargin = $this->pagedim[$this->page]['orm'];
02080                     $this->SetY($this->tMargin);
02081                 } else {
02082                     // account for booklet mode
02083                     if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
02084                         $deltam = $this->pagedim[$this->page]['olm'] - $this->pagedim[$this->page]['orm'];
02085                         $this->lMargin += $deltam;
02086                         $this->rMargin -= $deltam;
02087                     }
02088                 }
02089             } else {
02090                 $this->Error('Wrong page number on setPage() function.');
02091             }
02092         }
02093         
02101         public function lastPage($resetmargins=false) {
02102             $this->setPage($this->getNumPages(), $resetmargins);
02103         }
02104         
02112         public function getPage() {
02113             return $this->page;
02114         }
02115         
02116         
02124         public function getNumPages() {
02125             return $this->numpages;
02126         }
02127 
02137         public function AddPage($orientation='', $format='') {
02138             if (!isset($this->original_lMargin)) {
02139                 $this->original_lMargin = $this->lMargin;
02140             }
02141             if (!isset($this->original_rMargin)) {
02142                 $this->original_rMargin = $this->rMargin;
02143             }
02144             // terminate previous page
02145             $this->endPage();
02146             // start new page
02147             $this->startPage($orientation, $format);
02148         }
02149 
02156         protected function endPage() {
02157             // check if page is already closed
02158             if (($this->page == 0) OR ($this->numpages > $this->page) OR (!$this->pageopen[$this->page])) {
02159                 return;
02160             }
02161             $this->InFooter = true;
02162             // print page footer
02163             $this->setFooter();
02164             // close page
02165             $this->_endpage();
02166             // mark page as closed
02167             $this->pageopen[$this->page] = false;
02168             $this->InFooter = false;
02169         }
02170 
02180         protected function startPage($orientation='', $format='') {
02181             if ($this->numpages > $this->page) {
02182                 // this page has been already added
02183                 $this->setPage($this->page + 1);
02184                 $this->SetY($this->tMargin);
02185                 return;
02186             }
02187             // start a new page
02188             if ($this->state == 0) {
02189                 $this->Open();
02190             }
02191             ++$this->numpages;
02192             $this->swapMargins($this->booklet);
02193             // save current graphic settings
02194             $gvars = $this->getGraphicVars();
02195             // start new page
02196             $this->_beginpage($orientation, $format);
02197             // mark page as open
02198             $this->pageopen[$this->page] = true;
02199             // restore graphic settings
02200             $this->setGraphicVars($gvars);
02201             // mark this point
02202             $this->setPageMark();
02203             // print page header
02204             $this->setHeader();
02205             // restore graphic settings
02206             $this->setGraphicVars($gvars);
02207             // mark this point
02208             $this->setPageMark();
02209             // print table header (if any)
02210             $this->setTableHeader();
02211         }
02212             
02220         public function setPageMark() {
02221             $this->intmrk[$this->page] = $this->pagelen[$this->page];
02222         }
02223         
02232         public function setHeaderData($ln='', $lw=0, $ht='', $hs='') {
02233             $this->header_logo = $ln;
02234             $this->header_logo_width = $lw;
02235             $this->header_title = $ht;
02236             $this->header_string = $hs;
02237         }
02238         
02246         public function getHeaderData() {
02247             $ret = array();
02248             $ret['logo'] = $this->header_logo;
02249             $ret['logo_width'] = $this->header_logo_width;
02250             $ret['title'] = $this->header_title;
02251             $ret['string'] = $this->header_string;
02252             return $ret;
02253         }
02254         
02261         public function setHeaderMargin($hm=10) {
02262             $this->header_margin = $hm;
02263         }
02264         
02271         public function getHeaderMargin() {
02272             return $this->header_margin;
02273         }
02274         
02281         public function setFooterMargin($fm=10) {
02282             $this->footer_margin = $fm;
02283         }
02284         
02291         public function getFooterMargin() {
02292             return $this->footer_margin;
02293         }
02299         public function setPrintHeader($val=true) {
02300             $this->print_header = $val;
02301         }
02302         
02308         public function setPrintFooter($val=true) {
02309             $this->print_footer = $val;
02310         }
02311         
02317         public function getImageRBX() {
02318             return $this->img_rb_x;
02319         }
02320         
02326         public function getImageRBY() {
02327             return $this->img_rb_y;
02328         }
02329         
02335         public function Header() {
02336             $ormargins = $this->getOriginalMargins();
02337             $headerfont = $this->getHeaderFont();
02338             $headerdata = $this->getHeaderData();
02339             if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) {
02340                 $this->Image(K_PATH_IMAGES.$headerdata['logo'], $this->GetX(), $this->getHeaderMargin(), $headerdata['logo_width']);
02341                 $imgy = $this->getImageRBY();
02342             } else {
02343                 $imgy = $this->GetY();
02344             }
02345             $cell_height = round(($this->getCellHeightRatio() * $headerfont[2]) / $this->getScaleFactor(), 2);
02346             // set starting margin for text data cell
02347             if ($this->getRTL()) {
02348                 $header_x = $ormargins['right'] + ($headerdata['logo_width'] * 1.1);
02349             } else {
02350                 $header_x = $ormargins['left'] + ($headerdata['logo_width'] * 1.1);
02351             }
02352             $this->SetTextColor(0, 0, 0);
02353             // header title
02354             $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1);
02355             $this->SetX($header_x);         
02356             $this->Cell(0, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0);
02357             // header string
02358             $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]);
02359             $this->SetX($header_x);
02360             $this->MultiCell(0, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false);
02361             // print an ending header line
02362             $this->SetLineStyle(array('width' => 0.85 / $this->getScaleFactor(), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)));
02363             $this->SetY((2.835 / $this->getScaleFactor()) + max($imgy, $this->GetY()));
02364             if ($this->getRTL()) {
02365                 $this->SetX($ormargins['right']);
02366             } else {
02367                 $this->SetX($ormargins['left']);
02368             }
02369             $this->Cell(0, 0, '', 'T', 0, 'C');
02370         }
02371         
02377         public function Footer() {              
02378             $cur_y = $this->GetY();
02379             $ormargins = $this->getOriginalMargins();
02380             $this->SetTextColor(0, 0, 0);           
02381             //set style for cell border
02382             $line_width = 0.85 / $this->getScaleFactor();
02383             $this->SetLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)));
02384             //print document barcode
02385             $barcode = $this->getBarcode();
02386             if (!empty($barcode)) {
02387                 $this->Ln($line_width);
02388                 $barcode_width = round(($this->getPageWidth() - $ormargins['left'] - $ormargins['right'])/3);
02389                 $this->write1DBarcode($barcode, 'C128B', $this->GetX(), $cur_y + $line_width, $barcode_width, (($this->getFooterMargin() / 3) - $line_width), 0.3, '', ''); 
02390             }
02391             if (empty($this->pagegroups)) {
02392                 $pagenumtxt = $this->l['w_page'].' '.$this->getAliasNumPage().' / '.$this->getAliasNbPages();
02393             } else {
02394                 $pagenumtxt = $this->l['w_page'].' '.$this->getPageNumGroupAlias().' / '.$this->getPageGroupAlias();
02395             }       
02396             $this->SetY($cur_y);
02397             //Print page number
02398             if ($this->getRTL()) {
02399                 $this->SetX($ormargins['right']);
02400                 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L');
02401             } else {
02402                 $this->SetX($ormargins['left']);
02403                 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'R');
02404             }
02405         }
02406         
02412         protected function setHeader() {
02413             if ($this->print_header) {
02414                 $lasth = $this->lasth;
02415                 $this->_out('q');
02416                 $this->rMargin = $this->original_rMargin;
02417                 $this->lMargin = $this->original_lMargin;
02418                 $this->cMargin = 0;
02419                 //set current position
02420                 if ($this->rtl) {
02421                     $this->SetXY($this->original_rMargin, $this->header_margin);
02422                 } else {
02423                     $this->SetXY($this->original_lMargin, $this->header_margin);
02424                 }
02425                 $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]);
02426                 $this->Header();
02427                 //restore position
02428                 if ($this->rtl) {
02429                     $this->SetXY($this->original_rMargin, $this->tMargin);
02430                 } else {
02431                     $this->SetXY($this->original_lMargin, $this->tMargin);
02432                 }
02433                 $this->_out('Q');
02434                 $this->lasth = $lasth;
02435             }
02436         }
02437         
02443         protected function setFooter() {
02444             //Page footer
02445             // save current graphic settings
02446             $gvars = $this->getGraphicVars();
02447             // mark this point
02448             $this->footerpos[$this->page] = $this->pagelen[$this->page];
02449             $this->_out("\n");
02450             if ($this->print_footer) {
02451                 $lasth = $this->lasth;
02452                 $this->_out('q');
02453                 $this->rMargin = $this->original_rMargin;
02454                 $this->lMargin = $this->original_lMargin;
02455                 $this->cMargin = 0;
02456                 //set current position
02457                 $footer_y = $this->h - $this->footer_margin;
02458                 if ($this->rtl) {
02459                     $this->SetXY($this->original_rMargin, $footer_y);
02460                 } else {
02461                     $this->SetXY($this->original_lMargin, $footer_y);
02462                 }
02463                 $this->SetFont($this->footer_font[0], $this->footer_font[1], $this->footer_font[2]);
02464                 $this->Footer();
02465                 //restore position
02466                 if ($this->rtl) {
02467                     $this->SetXY($this->original_rMargin, $this->tMargin);
02468                 } else {
02469                     $this->SetXY($this->original_lMargin, $this->tMargin);
02470                 }
02471                 $this->_out('Q');
02472                 $this->lasth = $lasth;
02473             }
02474             // restore graphic settings
02475             $this->setGraphicVars($gvars);
02476             // calculate footer lenght
02477             $this->footerlen[$this->page] = $this->pagelen[$this->page] - $this->footerpos[$this->page] + 1;
02478         }
02479 
02485         protected function setTableHeader() {
02486             if (!$this->empty_string($this->theadMargin)) {
02487                 // restore the original top-margin
02488                 $this->tMargin = $this->theadMargin;
02489                 $this->pagedim[$this->page]['tm'] = $this->theadMargin;
02490                 $this->y = $this->theadMargin;
02491             }
02492             if (!$this->empty_string($this->thead)) {
02493                 // print table header
02494                 $this->writeHTML($this->thead, false, false, false, false, '');
02495                 // set new top margin to skip the table headers
02496                 if (!isset($this->theadMargin) OR ($this->empty_string($this->theadMargin))) {
02497                     $this->theadMargin = $this->tMargin;
02498                 }
02499                 $this->tMargin = $this->y;
02500                 $this->pagedim[$this->page]['tm'] = $this->tMargin;
02501             }
02502         }
02503         
02511         public function PageNo() {
02512             return $this->page;
02513         }
02514 
02527         public function AddSpotColor($name, $c, $m, $y, $k) {
02528             if (!isset($this->spot_colors[$name])) {
02529                 $i = 1 + count($this->spot_colors);
02530                 $this->spot_colors[$name] = array('i' => $i, 'c' => $c, 'm' => $m, 'y' => $y, 'k' => $k);
02531             }
02532         }
02533 
02543         public function SetDrawColorArray($color) {
02544             if (isset($color)) {
02545                 $color = array_values($color);
02546                 $r = isset($color[0]) ? $color[0] : -1;
02547                 $g = isset($color[1]) ? $color[1] : -1;
02548                 $b = isset($color[2]) ? $color[2] : -1;
02549                 $k = isset($color[3]) ? $color[3] : -1;
02550                 if ($r >= 0) {
02551                     $this->SetDrawColor($r, $g, $b, $k);
02552                 }
02553             }
02554         }
02555 
02566         public function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
02567             // set default values
02568             if (!is_numeric($col1)) {
02569                 $col1 = 0;
02570             }
02571             if (!is_numeric($col2)) {
02572                 $col2 = -1;
02573             }
02574             if (!is_numeric($col3)) {
02575                 $col3 = -1;
02576             }
02577             if (!is_numeric($col4)) {
02578                 $col4 = -1;
02579             }
02580             //Set color for all stroking operations
02581             if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
02582                 // Grey scale
02583                 $this->DrawColor = sprintf('%.3F G', $col1/255);
02584             } elseif ($col4 == -1) {
02585                 // RGB
02586                 $this->DrawColor = sprintf('%.3F %.3F %.3F RG', $col1/255, $col2/255, $col3/255);
02587             } else {
02588                 // CMYK
02589                 $this->DrawColor = sprintf('%.3F %.3F %.3F %.3F K', $col1/100, $col2/100, $col3/100, $col4/100);
02590             }
02591             if ($this->page > 0) {
02592                 $this->_out($this->DrawColor);
02593             }
02594         }
02595         
02604         public function SetDrawSpotColor($name, $tint=100) {
02605             if (!isset($this->spot_colors[$name])) {
02606                 $this->Error('Undefined spot color: '.$name);
02607             }
02608             $this->DrawColor = sprintf('/CS%d CS %.3F SCN', $this->spot_colors[$name]['i'], $tint/100);
02609             if ($this->page > 0) {
02610                 $this->_out($this->DrawColor);
02611             }
02612         }
02613         
02623         public function SetFillColorArray($color) {
02624             if (isset($color)) {
02625                 $color = array_values($color);
02626                 $r = isset($color[0]) ? $color[0] : -1;
02627                 $g = isset($color[1]) ? $color[1] : -1;
02628                 $b = isset($color[2]) ? $color[2] : -1;
02629                 $k = isset($color[3]) ? $color[3] : -1;
02630                 if ($r >= 0) {
02631                     $this->SetFillColor($r, $g, $b, $k);
02632                 }
02633             }
02634         }
02635         
02646         public function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
02647             // set default values
02648             if (!is_numeric($col1)) {
02649                 $col1 = 0;
02650             }
02651             if (!is_numeric($col2)) {
02652                 $col2 = -1;
02653             }
02654             if (!is_numeric($col3)) {
02655                 $col3 = -1;
02656             }
02657             if (!is_numeric($col4)) {
02658                 $col4 = -1;
02659             }
02660             //Set color for all filling operations
02661             if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
02662                 // Grey scale
02663                 $this->FillColor = sprintf('%.3F g', $col1/255);
02664                 $this->bgcolor = array('G' => $col1);
02665             } elseif ($col4 == -1) {
02666                 // RGB
02667                 $this->FillColor = sprintf('%.3F %.3F %.3F rg', $col1/255, $col2/255, $col3/255);
02668                 $this->bgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
02669             } else {
02670                 // CMYK
02671                 $this->FillColor = sprintf('%.3F %.3F %.3F %.3F k', $col1/100, $col2/100, $col3/100, $col4/100);
02672                 $this->bgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
02673             }
02674             $this->ColorFlag = ($this->FillColor != $this->TextColor);
02675             if ($this->page > 0) {
02676                 $this->_out($this->FillColor);
02677             }
02678         }
02679         
02688         public function SetFillSpotColor($name, $tint=100) {
02689             if (!isset($this->spot_colors[$name])) {
02690                 $this->Error('Undefined spot color: '.$name);
02691             }
02692             $this->FillColor = sprintf('/CS%d cs %.3F scn', $this->spot_colors[$name]['i'], $tint/100);
02693             $this->ColorFlag = ($this->FillColor != $this->TextColor);
02694             if ($this->page > 0) {
02695                 $this->_out($this->FillColor);
02696             }
02697         }
02698         
02707         public function SetTextColorArray($color) {
02708             if (isset($color)) {
02709                 $color = array_values($color);
02710                 $r = isset($color[0]) ? $color[0] : -1;
02711                 $g = isset($color[1]) ? $color[1] : -1;
02712                 $b = isset($color[2]) ? $color[2] : -1;
02713                 $k = isset($color[3]) ? $color[3] : -1;
02714                 if ($r >= 0) {
02715                     $this->SetTextColor($r, $g, $b, $k);
02716                 }
02717             }
02718         }
02719 
02730         public function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
02731             // set default values
02732             if (!is_numeric($col1)) {
02733                 $col1 = 0;
02734             }
02735             if (!is_numeric($col2)) {
02736                 $col2 = -1;
02737             }
02738             if (!is_numeric($col3)) {
02739                 $col3 = -1;
02740             }
02741             if (!is_numeric($col4)) {
02742                 $col4 = -1;
02743             }
02744             //Set color for text
02745             if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
02746                 // Grey scale
02747                 $this->TextColor = sprintf('%.3F g', $col1/255);
02748                 $this->fgcolor = array('G' => $col1);
02749             } elseif ($col4 == -1) {
02750                 // RGB
02751                 $this->TextColor = sprintf('%.3F %.3F %.3F rg', $col1/255, $col2/255, $col3/255);
02752                 $this->fgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
02753             } else {
02754                 // CMYK
02755                 $this->TextColor = sprintf('%.3F %.3F %.3F %.3F k', $col1/100, $col2/100, $col3/100, $col4/100);
02756                 $this->fgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
02757             }
02758             $this->ColorFlag = ($this->FillColor != $this->TextColor);
02759         }
02760         
02769         public function SetTextSpotColor($name, $tint=100) {
02770             if (!isset($this->spot_colors[$name])) {
02771                 $this->Error('Undefined spot color: '.$name);
02772             }
02773             $this->TextColor = sprintf('/CS%d cs %.3F scn', $this->spot_colors[$name]['i'], $tint/100);
02774             $this->ColorFlag = ($this->FillColor != $this->TextColor);
02775             if ($this->page > 0) {
02776                 $this->_out($this->TextColor);
02777             }
02778         }
02779 
02791         public function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0) {
02792             return $this->GetArrStringWidth($this->utf8Bidi($this->UTF8StringToArray($s), $s, $this->tmprtl), $fontname, $fontstyle, $fontsize);
02793         }
02794         
02806         public function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0) {
02807             // store current values
02808             if (!$this->empty_string($fontname)) {
02809                 $prev_FontFamily = $this->FontFamily;
02810                 $prev_FontStyle = $this->FontStyle;
02811                 $prev_FontSizePt = $this->FontSizePt;
02812                 $this->SetFont($fontname, $fontstyle, $fontsize);
02813             }
02814             $w = 0;
02815             foreach ($sa as $char) {
02816                 $w += $this->GetCharWidth($char);
02817             }
02818             // restore previous values
02819             if (!$this->empty_string($fontname)) {
02820                 $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt);
02821             }
02822             return $w;
02823         }
02824         
02833         public function GetCharWidth($char) {
02834             if ($char == 173) {
02835                 // SHY character will not be printed
02836                 return (0);
02837             }
02838             $cw = &$this->CurrentFont['cw'];
02839             if (isset($cw[$char])) {
02840                 $w = $cw[$char];
02841             } elseif (isset($this->CurrentFont['dw'])) {
02842                 // default width
02843                 $w = $this->CurrentFont['dw'];
02844             } elseif (isset($cw[32])) {
02845                 // default width
02846                 $dw = $cw[32];
02847             } else {
02848                 $w = 600;
02849             }
02850             return ($w * $this->FontSize / 1000);
02851         }
02852         
02860         public function GetNumChars($s) {
02861             if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
02862                 return count($this->UTF8StringToArray($s));
02863             } 
02864             return strlen($s);
02865         }
02866             
02872         protected function getFontsList() {
02873             $fontsdir = opendir($this->_getfontpath());
02874             while (($file = readdir($fontsdir)) !== false) {
02875                 if (substr($file, -4) == '.php') {
02876                     array_push($this->fontlist, strtolower(basename($file, '.php')));
02877                 }
02878             }
02879             closedir($fontsdir);
02880         }
02881         
02894         public function AddFont($family, $style='', $fontfile='') {
02895             if ($this->empty_string($family)) {
02896                 if (!$this->empty_string($this->FontFamily)) {
02897                     $family = $this->FontFamily;
02898                 } else {
02899                     $this->Error('Empty font family');
02900                 }
02901             }
02902             $family = strtolower($family);
02903             if ((!$this->isunicode) AND ($family == 'arial')) {
02904                 $family = 'helvetica';
02905             }
02906             if (($family == 'symbol') OR ($family == 'zapfdingbats')) {
02907                 $style = '';
02908             }
02909             $tempstyle = strtoupper($style);
02910             $style = '';
02911             // underline
02912             if (strpos($tempstyle, 'U') !== false) {
02913                 $this->underline = true;
02914             } else {
02915                 $this->underline = false;
02916             }
02917             // line through (deleted)
02918             if (strpos($tempstyle, 'D') !== false) {
02919                 $this->linethrough = true;
02920             } else {
02921                 $this->linethrough = false;
02922             }
02923             // bold
02924             if (strpos($tempstyle, 'B') !== false) {
02925                 $style .= 'B';
02926             }
02927             // oblique
02928             if (strpos($tempstyle, 'I') !== false) {
02929                 $style .= 'I';
02930             }
02931             $bistyle = $style;
02932             $fontkey = $family.$style;
02933             $font_style = $style.($this->underline ? 'U' : '').($this->linethrough ? 'D' : '');
02934             $fontdata = array('fontkey' => $fontkey, 'family' => $family, 'style' => $font_style);
02935             // check if the font has been already added
02936             if ($this->getFontBuffer($fontkey) !== false) {
02937                 return $fontdata;
02938             }
02939             if (isset($type)) {
02940                 unset($type); 
02941             }
02942             if (isset($cw)) {
02943                 unset($cw); 
02944             }
02945             // get specified font directory (if any)
02946             $fontdir = '';
02947             if (!$this->empty_string($fontfile)) {
02948                 $fontdir = dirname($fontfile);
02949                 if ($this->empty_string($fontdir) OR ($fontdir == '.')) {
02950                     $fontdir = '';
02951                 } else {
02952                     $fontdir .= '/';
02953                 }
02954             }
02955             // search and include font file
02956             if ($this->empty_string($fontfile) OR (!file_exists($fontfile))) {
02957                 // build a standard filenames for specified font
02958                 $fontfile1 = str_replace(' ', '', $family).strtolower($style).'.php';
02959                 $fontfile2 = str_replace(' ', '', $family).'.php';
02960                 // search files on various directories
02961                 if (file_exists($fontdir.$fontfile1)) {
02962                     $fontfile = $fontdir.$fontfile1;
02963                 } elseif (file_exists($this->_getfontpath().$fontfile1)) {
02964                     $fontfile = $this->_getfontpath().$fontfile1;
02965                 } elseif (file_exists($fontfile1)) {
02966                     $fontfile = $fontfile1;
02967                 } elseif (file_exists($fontdir.$fontfile2)) {
02968                     $fontfile = $fontdir.$fontfile2;
02969                 } elseif (file_exists($this->_getfontpath().$fontfile2)) {
02970                     $fontfile = $this->_getfontpath().$fontfile2;
02971                 } else {
02972                     $fontfile = $fontfile2;
02973                 }
02974             }
02975             // include font file
02976             if (file_exists($fontfile)) {
02977                 include($fontfile);
02978             } else {
02979                 $this->Error('Could not include font definition file: '.$family.'');
02980             }
02981             // check font parameters
02982             if ((!isset($type)) OR (!isset($cw))) {
02983                 $this->Error('The font definition file has a bad format: '.$fontfile.'');
02984             }
02985             if (!isset($file)) {
02986                 $file = '';
02987             }
02988             if (!isset($enc)) {
02989                 $enc = '';
02990             }
02991             if (!isset($dw) OR $this->empty_string($dw)) {
02992                 // set default width
02993                 if (isset($desc['MissingWidth']) AND ($desc['MissingWidth'] > 0)) {
02994                     $dw = $desc['MissingWidth'];
02995                 } elseif (isset($cw[32])) {
02996                     $dw = $cw[32];
02997                 } else {
02998                     $dw = 600;
02999                 }
03000             }
03001             ++$this->numfonts;          
03002             // register CID font (all styles at once)
03003             if ($type == 'cidfont0') {
03004                 $file = ''; // not embedded
03005                 $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic');
03006                 $sname = $name.$styles[$bistyle];
03007                 if ((strpos($bistyle, 'B') !== false) AND (isset($desc['StemV'])) AND ($desc['StemV'] == 70)) {
03008                     $desc['StemV'] = 120;
03009                 }
03010                 $this->setFontBuffer($fontkey, array('i' => $this->numfonts, 'type' => $type, 'name' => $sname, 'desc' => $desc, 'cidinfo' => $cidinfo, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'dw' => $dw, 'enc' => $enc));
03011             } elseif ($type == 'core') {
03012                 $this->setFontBuffer($fontkey, array('i' => $this->numfonts, 'type' => 'core', 'name' => $this->CoreFonts[$fontkey], 'up' => -100, 'ut' => 50, 'cw' => $cw, 'dw' => $dw));
03013             } elseif (($type == 'TrueType') OR ($type == 'Type1')) {
03014                 $this->setFontBuffer($fontkey, array('i' => $this->numfonts, 'type' => $type, 'name' => $name, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'dw' => $dw, 'file' => $file, 'enc' => $enc, 'desc' => $desc));
03015             } elseif ($type == 'TrueTypeUnicode') {
03016                 $this->setFontBuffer($fontkey, array('i' => $this->numfonts, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'dw' => $dw, 'enc' => $enc, 'file' => $file, 'ctg' => $ctg));
03017             } else {
03018                 $this->Error('Unknow font type: '.$type.'');
03019             }
03020             if (isset($diff) AND (!empty($diff))) {
03021                 //Search existing encodings
03022                 $d = 0;
03023                 $nb = count($this->diffs);
03024                 for($i=1; $i <= $nb; ++$i) {
03025                     if ($this->diffs[$i] == $diff) {
03026                         $d = $i;
03027                         break;
03028                     }
03029                 }
03030                 if ($d == 0) {
03031                     $d = $nb + 1;
03032                     $this->diffs[$d] = $diff;
03033                 }
03034                 $this->setFontSubBuffer($fontkey, 'diff', $d);
03035             }
03036             if (!$this->empty_string($file)) {
03037                 if ((strcasecmp($type,'TrueType') == 0) OR (strcasecmp($type, 'TrueTypeUnicode') == 0)) {
03038                     $this->FontFiles[$file] = array('length1' => $originalsize, 'fontdir' => $fontdir);
03039                 } elseif ($type != 'core') {
03040                     $this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2, 'fontdir' => $fontdir);
03041                 }
03042             }
03043             return $fontdata;
03044         }
03045 
03060         public function SetFont($family, $style='', $size=0, $fontfile='') {
03061             //Select a font; size given in points
03062             if ($size == 0) {
03063                 $size = $this->FontSizePt;
03064             }
03065             // try to add font (if not already added)
03066             $fontdata = $this->AddFont($family, $style, $fontfile);
03067             $this->FontFamily = $fontdata['family'];
03068             $this->FontStyle = $fontdata['style'];
03069             $this->CurrentFont = $this->getFontBuffer($fontdata['fontkey']);
03070             $this->SetFontSize($size);
03071         }
03072 
03080         public function SetFontSize($size) {
03081             //Set font size in points
03082             $this->FontSizePt = $size;
03083             $this->FontSize = $size / $this->k;
03084             if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) {
03085                 $this->FontAscent = $this->CurrentFont['desc']['Ascent'] * $this->FontSize / 1000;
03086             } else {
03087                 $this->FontAscent = 0.8 * $this->FontSize;
03088             }
03089             if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] > 0)) {
03090                 $this->FontDescent = - $this->CurrentFont['desc']['Descent'] * $this->FontSize / 1000;
03091             } else {
03092                 $this->FontDescent = 0.2 * $this->FontSize;
03093             }
03094             if (($this->page > 0) AND (isset($this->CurrentFont['i']))) {
03095                 $this->_out(sprintf('BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
03096             }
03097         }
03098 
03105         public function SetDefaultMonospacedFont($font) {
03106             $this->default_monospaced_font = $font;
03107         }
03108         
03116         public function AddLink() {
03117             //Create a new internal link
03118             $n = count($this->links) + 1;
03119             $this->links[$n] = array(0, 0);
03120             return $n;
03121         }
03122 
03132         public function SetLink($link, $y=0, $page=-1) {
03133             if ($y == -1) {
03134                 $y = $this->y;
03135             }
03136             if ($page == -1) {
03137                 $page = $this->page;
03138             }
03139             $this->links[$link] = array($page, $y);
03140         }
03141 
03155         public function Link($x, $y, $w, $h, $link, $spaces=0) {
03156             $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'), $spaces);
03157         }
03158         
03172         public function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text'), $spaces=0) {
03173             // recalculate coordinates to account for graphic transformations
03174             if (isset($this->transfmatrix)) {
03175                 $maxid = count($this->transfmatrix) - 1;
03176                 for ($i=$maxid; $i >= 0; $i--) {
03177                     $ctm = $this->transfmatrix[$i];
03178                     if (isset($ctm['a'])) {
03179                         $x = $x * $this->k;
03180                         $y = ($this->h - $y) * $this->k;
03181                         $w = $w * $this->k;
03182                         $h = $h * $this->k;
03183                         // top left
03184                         $xt = $x;
03185                         $yt = $y;
03186                         $x1 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
03187                         $y1 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
03188                         // top right
03189                         $xt = $x + $w;
03190                         $yt = $y;
03191                         $x2 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
03192                         $y2 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
03193                         // bottom left
03194                         $xt = $x;
03195                         $yt = $y - $h;
03196                         $x3 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
03197                         $y3 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
03198                         // bottom right
03199                         $xt = $x + $w;
03200                         $yt = $y - $h;
03201                         $x4 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
03202                         $y4 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
03203                         // new coordinates (rectangle area)
03204                         $x = min($x1, $x2, $x3, $x4);
03205                         $y = max($y1, $y2, $y3, $y4);
03206                         $w = (max($x1, $x2, $x3, $x4) - $x) / $this->k;
03207                         $h = ($y - min($y1, $y2, $y3, $y4)) / $this->k;
03208                         $x = $x / $this->k;
03209                         $y = $this->h - ($y / $this->k);
03210                     }
03211                 }
03212             }
03213             $this->PageAnnots[$this->page][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces);
03214             if (($opt['Subtype'] == 'FileAttachment') AND (!$this->empty_string($opt['FS'])) AND file_exists($opt['FS']) AND (!isset($this->embeddedfiles[basename($opt['FS'])]))) {
03215                 $this->embeddedfiles[basename($opt['FS'])] = array('file' => $opt['FS'], 'n' => ($this->n + count($this->embeddedfiles) + 10000));
03216             }
03217         }
03218 
03225         protected function _putEmbeddedFiles() {
03226             reset($this->embeddedfiles);
03227             foreach ($this->embeddedfiles as $filename => $filedata) {
03228                 $data = file_get_contents($filedata['file']);
03229                 $filter = '';
03230                 if ($this->compress) {
03231                     $data = gzcompress($data);
03232                     $filter = ' /Filter /FlateDecode';
03233                 }
03234                 $this->offsets[$filedata['n']] = $this->bufferlen;
03235                 $this->_out($filedata['n'].' 0 obj');
03236                 $this->_out('<</Type /EmbeddedFile'.$filter.' /Length '.strlen($data).' >>');
03237                 $this->_putstream($data);
03238                 $this->_out('endobj');
03239             }
03240         }
03241         
03256         public function Text($x, $y, $txt, $stroke=0, $clip=false) {
03257             //Output a string
03258             if ($this->rtl) {
03259                 // bidirectional algorithm (some chars may be changed affecting the line length)
03260                 $s = $this->utf8Bidi($this->UTF8StringToArray($txt), $txt, $this->tmprtl);
03261                 $l = $this->GetArrStringWidth($s);
03262                 $xr = $this->w - $x - $this->GetArrStringWidth($s);
03263             } else {
03264                 $xr = $x;
03265             }
03266             $opt = '';
03267             if (($stroke > 0) AND (!$clip)) {
03268                 $opt .= '1 Tr '.intval($stroke).' w ';
03269             } elseif (($stroke > 0) AND $clip) {
03270                 $opt .= '5 Tr '.intval($stroke).' w ';
03271             } elseif ($clip) {
03272                 $opt .= '7 Tr ';
03273             }
03274             $s = sprintf('BT %.2F %.2F Td %s(%s) Tj ET 0 Tr', $xr * $this->k, ($this->h-$y) * $this->k, $opt, $this->_escapetext($txt));
03275             if ($this->underline AND ($txt!='')) {
03276                 $s .= ' '.$this->_dounderline($xr, $y, $txt);
03277             }
03278             if ($this->linethrough AND ($txt!='')) { 
03279                 $s .= ' '.$this->_dolinethrough($xr, $y, $txt); 
03280             }
03281             if ($this->ColorFlag AND (!$clip)) {
03282                 $s='q '.$this->TextColor.' '.$s.' Q';
03283             }
03284             $this->_out($s);
03285         }
03286 
03296         public function AcceptPageBreak() {
03297             return $this->AutoPageBreak;
03298         }
03299         
03307         protected function checkPageBreak($h) {
03308             if ((($this->y + $h) > $this->PageBreakTrigger) AND (!$this->InFooter) AND ($this->AcceptPageBreak())) {
03309                 //Automatic page break
03310                 $x = $this->x;
03311                 $this->AddPage($this->CurOrientation);
03312                 $this->y = $this->tMargin;
03313                 $oldpage = $this->page - 1;
03314                 if ($this->rtl) {
03315                     if ($this->pagedim[$this->page]['orm'] != $this->pagedim[$oldpage]['orm']) {
03316                         $this->x = $x - ($this->pagedim[$this->page]['orm'] - $this->pagedim[$oldpage]['orm']);
03317                     } else {
03318                         $this->x = $x;
03319                     }
03320                 } else {
03321                     if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
03322                         $this->x = $x + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$oldpage]['olm']);
03323                     } else {
03324                         $this->x = $x;
03325                     }
03326                 }
03327                 return true;
03328             }
03329             return false;
03330         }
03331 
03350         public function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0, $ignore_min_height=false) {
03351             //$min_cell_height = $this->FontAscent + $this->FontDescent;
03352             $min_cell_height = $this->FontSize * $this->cell_height_ratio;
03353             if ($h < $min_cell_height) {
03354                 $h = $min_cell_height;
03355             }
03356             $this->checkPageBreak($h);
03357             $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height));
03358         }
03359 
03367         public function removeSHY($txt='') {
03368             /*
03369             * Unicode Data
03370             * Name : SOFT HYPHEN, commonly abbreviated as SHY
03371             * HTML Entity (decimal): &#173;
03372             * HTML Entity (hex): &#xad;
03373             * HTML Entity (named): &shy;
03374             * How to type in Microsoft Windows: [Alt +00AD] or [Alt 0173]
03375             * UTF-8 (hex): 0xC2 0xAD (c2ad)
03376             * UTF-8 character: chr(194).chr(173)
03377             */
03378             $txt = preg_replace('/([\\xc2]{1}[\\xad]{1})/', '', $txt);
03379             if (!$this->isunicode) {
03380                 $txt = preg_replace('/([\\xad]{1})/', '', $txt);
03381             }
03382             return $txt;
03383         }
03384         
03402         protected function getCellCode($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0, $ignore_min_height=false) {
03403             $txt = $this->removeSHY($txt);
03404             $rs = ''; //string to be returned
03405             if (!$ignore_min_height) {
03406                 $min_cell_height = $this->FontSize * $this->cell_height_ratio;
03407                 if ($h < $min_cell_height) {
03408                     $h = $min_cell_height;
03409                 }
03410             }
03411             $k = $this->k;
03412             if ($this->empty_string($w) OR ($w <= 0)) {
03413                 if ($this->rtl) {
03414                     $w = $this->x - $this->lMargin;
03415                 } else {
03416                     $w = $this->w - $this->rMargin - $this->x;
03417                 }
03418             }
03419             $s = '';            
03420             if (($fill == 1) OR ($border == 1)) {
03421                 if ($fill == 1) {
03422                     $op = ($border == 1) ? 'B' : 'f';
03423                 } else {
03424                     $op = 'S';
03425                 }
03426                 if ($this->rtl) {
03427                     $xk = (($this->x  - $w) * $k);
03428                 } else {
03429                     $xk = ($this->x * $k);
03430                 }
03431                 $s .= sprintf('%.2F %.2F %.2F %.2F re %s ', $xk, (($this->h - $this->y) * $k), ($w * $k), (-$h * $k), $op);
03432             }
03433             if (is_string($border)) {
03434                 $lm = ($this->LineWidth / 2);
03435                 $x = $this->x;
03436                 $y = $this->y;
03437                 if (strpos($border,'L') !== false) {
03438                     if ($this->rtl) {
03439                         $xk = ($x - $w) * $k;
03440                     } else {
03441                         $xk = $x * $k;
03442                     }
03443                     $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - $y + $lm) * $k), $xk, (($this->h - ($y + $h + $lm)) * $k));
03444                 }
03445                 if (strpos($border,'T') !== false) {
03446                     if ($this->rtl) {
03447                         $xk = ($x - $w + $lm) * $k;
03448                         $xwk = ($x - $lm) * $k;
03449                     } else {
03450                         $xk = ($x - $lm) * $k;
03451                         $xwk = ($x + $w + $lm) * $k;
03452                     }
03453                     $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - $y) * $k), $xwk, (($this->h - $y) * $k));
03454                 }
03455                 if (strpos($border,'R') !== false) {
03456                     if ($this->rtl) {
03457                         $xk = $x * $k;
03458                     } else {
03459                         $xk = ($x + $w) * $k;
03460                     }
03461                     $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - $y + $lm) * $k), $xk, (($this->h - ($y + $h + $lm))* $k));
03462                 }
03463                 if (strpos($border,'B') !== false) {
03464                     if ($this->rtl) {
03465                         $xk = ($x - $w + $lm) * $k;
03466                         $xwk = ($x - $lm) * $k;
03467                     } else {
03468                         $xk = ($x - $lm) * $k;
03469                         $xwk = ($x + $w + $lm) * $k;
03470                     }
03471                     $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - ($y + $h)) * $k), $xwk, (($this->h - ($y + $h)) * $k));
03472                 }
03473             }
03474             if ($txt != '') {
03475                 // text lenght
03476                 $width = $this->GetStringWidth($txt);
03477                 // ratio between cell lenght and text lenght
03478                 $ratio = ($w - (2 * $this->cMargin)) / $width;
03479                 
03480                 // stretch text if required
03481                 if (($stretch > 0) AND (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0)))) {
03482                     if ($stretch > 2) {
03483                         // spacing
03484                         //Calculate character spacing in points
03485                         $char_space = (($w - $width - (2 * $this->cMargin)) * $this->k) / max($this->GetNumChars($txt)-1,1);
03486                         //Set character spacing
03487                         $rs .= sprintf('BT %.2F Tc ET ', $char_space);
03488                     } else {
03489                         // scaling
03490                         //Calculate horizontal scaling
03491                         $horiz_scale = $ratio * 100.0;
03492                         //Set horizontal scaling
03493                         $rs .= sprintf('BT %.2F Tz ET ', $horiz_scale);
03494                     }
03495                     $align = '';
03496                     $width = $w - (2 * $this->cMargin);
03497                 } else {
03498                     $stretch == 0;
03499                 }
03500                 if ($align == 'L') {
03501                     if ($this->rtl) {
03502                         $dx = $w - $width - $this->cMargin;
03503                     } else {
03504                         $dx = $this->cMargin;
03505                     }
03506                 } elseif ($align == 'R') {
03507                     if ($this->rtl) {
03508                         $dx = $this->cMargin;
03509                     } else {
03510                         $dx = $w - $width - $this->cMargin;
03511                     }
03512                 } elseif ($align == 'C') {
03513                     $dx = ($w - $width) / 2;
03514                 } elseif ($align == 'J') {
03515                     if ($this->rtl) {
03516                         $dx = $w - $width - $this->cMargin;
03517                     } else {
03518                         $dx = $this->cMargin;
03519                     }
03520                 } else {
03521                     $dx = $this->cMargin;
03522                 }
03523                 if ($this->ColorFlag) {
03524                     $s .= 'q '.$this->TextColor.' ';
03525                 }
03526                 $txt2 = $this->_escapetext($txt);
03527                 if ($this->rtl) {
03528                     $xdk = ($this->x - $dx - $width) * $k;
03529                 } else {
03530                     $xdk = ($this->x + $dx) * $k;
03531                 }
03532                 // Justification
03533                 if ($align == 'J') {
03534                     // count number of spaces
03535                     $ns = substr_count($txt, ' ');
03536                     if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
03537                         // get string width without spaces
03538                         $width = $this->GetStringWidth(str_replace(' ', '', $txt));
03539                         // calculate average space width
03540                         $spacewidth = ($w - $width - (2 * $this->cMargin)) / ($ns?$ns:1) / $this->FontSize / $this->k;
03541                         // set word position to be used with TJ operator
03542                         $txt2 = str_replace(chr(0).' ', ') '.(-2830 * $spacewidth).' (', $txt2);
03543                     } else {
03544                         // get string width
03545                         $width = $this->GetStringWidth($txt);
03546                         $spacewidth = (($w - $width - (2 * $this->cMargin)) / ($ns?$ns:1)) * $this->k;
03547                         $rs .= sprintf('BT %.3F Tw ET ', $spacewidth);
03548                     }
03549                 }
03550                 // calculate approximate position of the font base line
03551                 //$basefonty = $this->y + (($h + $this->FontAscent - $this->FontDescent)/2);
03552                 $basefonty = $this->y + ($h/2) + ($this->FontSize/3);
03553                 // print text
03554                 $s .= sprintf('BT %.2F %.2F Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2);
03555                 if ($this->rtl) {
03556                     $xdx = $this->x - $dx - $width;
03557                 } else {
03558                     $xdx = $this->x + $dx;
03559                 }
03560                 if ($this->underline)  {
03561                     $s .= ' '.$this->_dounderline($xdx, $basefonty, $txt);
03562                 }
03563                 if ($this->linethrough) { 
03564                     $s .= ' '.$this->_dolinethrough($xdx, $basefonty, $txt);
03565                 }
03566                 if ($this->ColorFlag) {
03567                     $s .= ' Q';
03568                 }
03569                 if ($link) {
03570                     $this->Link($xdx, $this->y + (($h - $this->FontSize)/2), $width, $this->FontSize, $link, substr_count($txt, chr(32)));
03571                 }
03572             }
03573             // output cell
03574             if ($s) {
03575                 // output cell
03576                 $rs .= $s;
03577                 // reset text stretching
03578                 if ($stretch > 2) {
03579                     //Reset character horizontal spacing
03580                     $rs .= ' BT 0 Tc ET';
03581                 } elseif ($stretch > 0) {
03582                     //Reset character horizontal scaling
03583                     $rs .= ' BT 100 Tz ET';
03584                 }
03585             }
03586             // reset word spacing
03587             if (!(($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) AND ($align == 'J')) {
03588                 $rs .= ' BT 0 Tw ET';
03589             }
03590             $this->lasth = $h;
03591             if ($ln > 0) {
03592                 //Go to the beginning of the next line
03593                 $this->y += $h;
03594                 if ($ln == 1) {
03595                     if ($this->rtl) {
03596                         $this->x = $this->w - $this->rMargin;
03597                     } else {
03598                         $this->x = $this->lMargin;
03599                     }
03600                 }
03601             } else {
03602                 // go left or right by case
03603                 if ($this->rtl) {
03604                     $this->x -= $w;
03605                 } else {
03606                     $this->x += $w;
03607                 }
03608             }
03609             $gstyles = ''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor."\n";
03610             $rs = $gstyles.$rs;
03611             return $rs;
03612         }
03613 
03637         public function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=0, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0) { 
03638             if ($this->empty_string($this->lasth) OR $reseth) {
03639                 //set row height
03640                 $this->lasth = $this->FontSize * $this->cell_height_ratio;
03641             }
03642             if (!$this->empty_string($y)) {
03643                 $this->SetY($y);
03644             } else {
03645                 $y = $this->GetY();
03646             }
03647             // check for page break
03648             $this->checkPageBreak($h);
03649             $y = $this->GetY();
03650             // get current page number
03651             $startpage = $this->page;
03652             if (!$this->empty_string($x)) {
03653                 $this->SetX($x);
03654             } else {
03655                 $x = $this->GetX();
03656             }
03657             if ($this->empty_string($w) OR ($w <= 0)) {
03658                 if ($this->rtl) {
03659                     $w = $this->x - $this->lMargin;
03660                 } else {
03661                     $w = $this->w - $this->rMargin - $this->x;
03662                 }
03663             }
03664             // store original margin values
03665             $lMargin = $this->lMargin;
03666             $rMargin = $this->rMargin;
03667             if ($this->rtl) {
03668                 $this->SetRightMargin($this->w - $this->x);
03669                 $this->SetLeftMargin($this->x - $w);
03670             } else {
03671                 $this->SetLeftMargin($this->x);
03672                 $this->SetRightMargin($this->w - $this->x - $w);
03673             }
03674             $starty = $this->y;
03675             if ($autopadding) {
03676                 // Adjust internal padding
03677                 if ($this->cMargin < ($this->LineWidth / 2)) {
03678                     $this->cMargin = ($this->LineWidth / 2);
03679                 }
03680                 // Add top space if needed
03681                 if (($this->lasth - $this->FontSize) < $this->LineWidth) {
03682                     $this->y += $this->LineWidth / 2;
03683                 }
03684                 // add top padding
03685                 $this->y += $this->cMargin;
03686             }
03687             if ($ishtml) {
03688                 // ******* Write HTML text
03689                 $this->writeHTML($txt, true, 0, $reseth, true, $align);
03690                 $nl = 1;
03691             } else {
03692                 // ******* Write text
03693                 $nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false, false, $maxh);
03694             }
03695             if ($autopadding) {
03696                 // add bottom padding
03697                 $this->y += $this->cMargin;
03698                 // Add bottom space if needed
03699                 if (($this->lasth - $this->FontSize) < $this->LineWidth) {
03700                     $this->y += $this->LineWidth / 2;
03701                 }
03702             }
03703             // Get end-of-text Y position
03704             $currentY = $this->y;
03705             // get latest page number
03706             $endpage = $this->page;
03707             // check if a new page has been created
03708             if ($endpage > $startpage) {
03709                 // design borders around HTML cells.
03710                 for ($page=$startpage; $page <= $endpage; ++$page) {
03711                     $this->setPage($page);
03712                     if ($page == $startpage) {
03713                         $this->y = $starty; // put cursor at the beginning of cell on the first page
03714                         $h = $this->getPageHeight() - $starty - $this->getBreakMargin();
03715                         $cborder = $this->getBorderMode($border, $position='start');
03716                     } elseif ($page == $endpage) {
03717                         $this->y = $this->tMargin; // put cursor at the beginning of last page
03718                         $h = $currentY - $this->tMargin;
03719                         $cborder = $this->getBorderMode($border, $position='end');
03720                     } else {
03721                         $this->y = $this->tMargin; // put cursor at the beginning of the current page
03722                         $h = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin();
03723                         $cborder = $this->getBorderMode($border, $position='middle');
03724                     }
03725                     $nx = $x;
03726                     // account for margin changes
03727                     if ($page > $startpage) {
03728                         if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
03729                             $nx = $x + ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
03730                         } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
03731                             $nx = $x + ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
03732                         }
03733                     }
03734                     $this->SetX($nx);
03735                     $ccode = $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, false);
03736                     if ($cborder OR $fill) {
03737                         $pagebuff = $this->getPageBuffer($this->page);
03738                         $pstart = substr($pagebuff, 0, $this->intmrk[$this->page]);
03739                         $pend = substr($pagebuff, $this->intmrk[$this->page]);
03740                         $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend);
03741                         $this->intmrk[$this->page] += strlen($ccode."\n");
03742                     }
03743                 }
03744             } else {
03745                 $h = max($h, ($currentY - $y));
03746                 // put cursor at the beginning of text
03747                 $this->SetY($y); 
03748                 $this->SetX($x);
03749                 // design a cell around the text
03750                 $ccode = $this->getCellCode($w, $h, '', $border, 1, '', $fill, '', 0, true);
03751                 if ($border OR $fill) {
03752                     if (end($this->transfmrk[$this->page]) !== false) {
03753                         $pagemarkkey = key($this->transfmrk[$this->page]);
03754                         $pagemark = &$this->transfmrk[$this->page][$pagemarkkey];
03755                     } elseif ($this->InFooter) {
03756                         $pagemark = &$this->footerpos[$this->page];
03757                     } else {
03758                         $pagemark = &$this->intmrk[$this->page];
03759                     }
03760                     $pagebuff = $this->getPageBuffer($this->page);
03761                     $pstart = substr($pagebuff, 0, $pagemark);
03762                     $pend = substr($pagebuff, $pagemark);
03763                     $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend);
03764                     $pagemark += strlen($ccode."\n");
03765                 }
03766             }
03767             // Get end-of-cell Y position
03768             $currentY = $this->GetY();
03769             // restore original margin values
03770             $this->SetLeftMargin($lMargin);
03771             $this->SetRightMargin($rMargin);
03772             if ($ln > 0) {
03773                 //Go to the beginning of the next line
03774                 $this->SetY($currentY);
03775                 if ($ln == 2) {
03776                     $this->SetX($x + $w);
03777                 }
03778             } else {
03779                 // go left or right by case
03780                 $this->setPage($startpage);
03781                 $this->y = $y;
03782                 $this->SetX($x + $w);
03783             }
03784             return $nl;
03785         }
03786 
03795         protected function getBorderMode($border, $position='start') {
03796             if ((!$this->opencell) AND ($border == 1)) {
03797                 return 1;
03798             }
03799             $cborder = '';
03800             switch ($position) {
03801                 case 'start': {
03802                     if ($border == 1) {
03803                         $cborder = 'LTR';
03804                     } else {
03805                         if (!(false === strpos($border, 'L'))) {
03806                             $cborder .= 'L';
03807                         }
03808                         if (!(false === strpos($border, 'T'))) {
03809                             $cborder .= 'T';
03810                         }
03811                         if (!(false === strpos($border, 'R'))) {
03812                             $cborder .= 'R';
03813                         }
03814                         if ((!$this->opencell) AND (!(false === strpos($border, 'B')))) {
03815                             $cborder .= 'B';
03816                         }
03817                     }
03818                     break;
03819                 }
03820                 case 'middle': {
03821                     if ($border == 1) {
03822                         $cborder = 'LR';
03823                     } else {
03824                         if (!(false === strpos($border, 'L'))) {
03825                             $cborder .= 'L';
03826                         }
03827                         if ((!$this->opencell) AND (!(false === strpos($border, 'T')))) {
03828                             $cborder .= 'T';
03829                         }
03830                         if (!(false === strpos($border, 'R'))) {
03831                             $cborder .= 'R';
03832                         }
03833                         if ((!$this->opencell) AND (!(false === strpos($border, 'B')))) {
03834                             $cborder .= 'B';
03835                         }
03836                     }
03837                     break;
03838                 }
03839                 case 'end': {
03840                     if ($border == 1) {
03841                         $cborder = 'LRB';
03842                     } else {
03843                         if (!(false === strpos($border, 'L'))) {
03844                             $cborder .= 'L';
03845                         }
03846                         if ((!$this->opencell) AND (!(false === strpos($border, 'T')))) {
03847                             $cborder .= 'T';
03848                         }
03849                         if (!(false === strpos($border, 'R'))) {
03850                             $cborder .= 'R';
03851                         }
03852                         if (!(false === strpos($border, 'B'))) {
03853                             $cborder .= 'B';
03854                         }
03855                     }
03856                     break;
03857                 }
03858                 default: {
03859                     $cborder = $border;
03860                     break;
03861                 }
03862             }
03863             return $cborder;
03864         }
03865 
03874         public function getNumLines($txt, $w=0) {
03875             $lines = 0;
03876             if ($this->empty_string($w) OR ($w <= 0)) {
03877                 if ($this->rtl) {
03878                     $w = $this->x - $this->lMargin;
03879                 } else {
03880                     $w = $this->w - $this->rMargin - $this->x;
03881                 }
03882             }
03883             // max column width
03884             $wmax = $w - (2 * $this->cMargin);
03885             // remove carriage returns
03886             $txt = str_replace("\r", '', $txt);
03887             // remove last newline (if any)
03888             if (substr($txt,-1) == "\n") {
03889                 $txt = substr($txt, 0, -1);
03890             }
03891             // divide text in blocks
03892             $txtblocks = explode("\n", $txt);
03893             // for each block;
03894             foreach ($txtblocks as $block) {
03895                 // estimate the number of lines
03896                 $lines += $this->empty_string($block) ? 1 : (ceil($this->GetStringWidth($block) / $wmax));
03897             }
03898             return $lines;
03899         }
03900             
03917         public function Write($h, $txt, $link='', $fill=0, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0) {
03918             if (strlen($txt) == 0) {
03919                 $txt = ' ';
03920             }
03921             // remove carriage returns
03922             $s = str_replace("\r", '', $txt);
03923             // check if string contains arabic text
03924             if (preg_match(K_RE_PATTERN_ARABIC, $s)) {
03925                 $arabic = true;
03926             } else {
03927                 $arabic = false;
03928             }
03929             // check if string contains RTL text
03930             if ($arabic OR $this->tmprtl OR preg_match(K_RE_PATTERN_RTL, $txt)) {
03931                 $rtlmode = true;
03932             } else {
03933                 $rtlmode = false;
03934             }
03935             // get a char width
03936             $chrwidth = $this->GetCharWidth('.');
03937             // get array of unicode values
03938             $chars = $this->UTF8StringToArray($s);
03939             // get array of chars
03940             $uchars = $this->UTF8ArrayToUniArray($chars);
03941             // get the number of characters
03942             $nb = count($chars);
03943             // replacement for SHY character (minus symbol)
03944             $shy_replacement = 45;
03945             $shy_replacement_char = $this->unichr($shy_replacement);
03946             // widht for SHY replacement
03947             $shy_replacement_width = $this->GetCharWidth($shy_replacement);
03948             // store current position
03949             $prevx = $this->x;
03950             $prevy = $this->y;
03951             // max Y
03952             $maxy = $this->y + $maxh - $h - (2 * $this->cMargin);
03953             // calculate remaining line width ($w)
03954             if ($this->rtl) {
03955                 $w = $this->x - $this->lMargin;
03956             } else {
03957                 $w = $this->w - $this->rMargin - $this->x;
03958             }
03959             // max column width
03960             $wmax = $w - (2 * $this->cMargin);
03961             $i = 0; // character position
03962             $j = 0; // current starting position
03963             $sep = -1; // position of the last blank space
03964             $shy = false; // true if the last blank is a soft hypen (SHY)
03965             $l = 0; // current string lenght
03966             $nl = 0; //number of lines
03967             $linebreak = false;
03968             // for each character
03969             while ($i < $nb) {
03970                 if (($maxh > 0) AND ($this->y >= $maxy) ) {
03971                     $firstline = true;
03972                 }
03973                 //Get the current character
03974                 $c = $chars[$i];
03975                 if ($c == 10) { // 10 = "\n" = new line
03976                     //Explicit line break
03977                     if ($align == 'J') {
03978                         if ($this->rtl) {
03979                             $talign = 'R';
03980                         } else {
03981                             $talign = 'L';
03982                         }
03983                     } else {
03984                         $talign = $align;
03985                     }
03986                     $tmpstr = $this->UniArrSubString($uchars, $j, $i);
03987                     if ($firstline) {
03988                         $startx = $this->x;
03989                         $tmparr = array_slice($chars, $j, $i);
03990                         if ($rtlmode) {
03991                             $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
03992                         }
03993                         $linew = $this->GetArrStringWidth($tmparr);
03994                         unset($tmparr);
03995                         if ($this->rtl) {
03996                             $this->endlinex = $startx - $linew;
03997                         } else {
03998                             $this->endlinex = $startx + $linew;
03999                         }
04000                         $w = $linew;
04001                         $tmpcmargin = $this->cMargin;
04002                         if ($maxh == 0) {
04003                             $this->cMargin = 0;
04004                         }
04005                     }
04006                     $this->Cell($w, $h, $tmpstr, 0, 1, $talign, $fill, $link, $stretch);
04007                     unset($tmpstr);
04008                     if ($firstline) {
04009                         $this->cMargin = $tmpcmargin;
04010                         return ($this->UniArrSubString($uchars, $i));
04011                     }
04012                     ++$nl;
04013                     $j = $i + 1;
04014                     $l = 0;
04015                     $sep = -1;
04016                     $shy = false;
04017                     // account for margin changes
04018                     if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND (!$this->InFooter)) {
04019                         // AcceptPageBreak() may be overriden on extended classed to include margin changes
04020                         $this->AcceptPageBreak();
04021                     }
04022                     $w = $this->getRemainingWidth();
04023                     $wmax = $w - (2 * $this->cMargin);
04024                 } else {
04025                     // 160 is the non-breaking space.
04026                     // 173 is SHY (Soft Hypen).
04027                     // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
04028                     // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
04029                     // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
04030                     if (($c != 160) AND (($c == 173) OR preg_match($this->re_spaces, $this->unichr($c)))) {
04031                         // update last blank space position
04032                         $sep = $i;
04033                         // check if is a SHY
04034                         if ($c == 173) {
04035                             $shy = true;
04036                         } else {
04037                             $shy = false;
04038                         }
04039                     }
04040                     // update string length
04041                     if ((($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) AND ($arabic)) {
04042                         // with bidirectional algorithm some chars may be changed affecting the line length
04043                         // *** very slow ***
04044                         $l = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $i-$j+1), '', $this->tmprtl));
04045                     } else {
04046                         $l += $this->GetCharWidth($c);
04047                     }
04048                     if (($l > $wmax) OR ($shy AND (($l + $shy_replacement_width) > $wmax)) ) {
04049                         // we have reached the end of column
04050                         if ($sep == -1) {
04051                             // check if the line was already started
04052                             if (($this->rtl AND ($this->x <= ($this->w - $this->rMargin - $chrwidth)))
04053                                 OR ((!$this->rtl) AND ($this->x >= ($this->lMargin + $chrwidth)))) {
04054                                 // print a void cell and go to next line
04055                                 $this->Cell($w, $h, '', 0, 1);
04056                                 $linebreak = true;
04057                                 if ($firstline) {
04058                                     return ($this->UniArrSubString($uchars, $j));
04059                                 }
04060                             } else {
04061                                 // truncate the word because do not fit on column
04062                                 $tmpstr = $this->UniArrSubString($uchars, $j, $i);
04063                                 if ($firstline) {
04064                                     $startx = $this->x;
04065                                     $tmparr = array_slice($chars, $j, $i);
04066                                     if ($rtlmode) {
04067                                         $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
04068                                     }
04069                                     $linew = $this->GetArrStringWidth($tmparr);
04070                                     unset($tmparr);
04071                                     if ($this->rtl) {
04072                                         $this->endlinex = $startx - $linew;
04073                                     } else {
04074                                         $this->endlinex = $startx + $linew;
04075                                     }
04076                                     $w = $linew;
04077                                     $tmpcmargin = $this->cMargin;
04078                                     if ($maxh == 0) {
04079                                         $this->cMargin = 0;
04080                                     }
04081                                 }
04082                                 $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
04083                                 unset($tmpstr);
04084                                 if ($firstline) {
04085                                     $this->cMargin = $tmpcmargin;
04086                                     return ($this->UniArrSubString($uchars, $i));
04087                                 }
04088                                 $j = $i;
04089                                 --$i;
04090                             }   
04091                         } else {
04092                             // word wrapping
04093                             if ($this->rtl AND (!$firstblock)) {
04094                                 $endspace = 1;
04095                             } else {
04096                                 $endspace = 0;
04097                             }
04098                             if ($shy) {
04099                                 // add hypen (minus symbol) at the end of the line
04100                                 $shy_width = $shy_replacement_width;
04101                                 if ($this->rtl) {
04102                                     $shy_char_left = $shy_replacement_char;
04103                                     $shy_char_right = '';
04104                                 } else {
04105                                     $shy_char_left = '';
04106                                     $shy_char_right = $shy_replacement_char;
04107                                 }
04108                             } else {
04109                                 $shy_width = 0;
04110                                 $shy_char_left = '';
04111                                 $shy_char_right = '';
04112                             }
04113                             $tmpstr = $this->UniArrSubString($uchars, $j, ($sep + $endspace));
04114                             if ($firstline) {
04115                                 $startx = $this->x;
04116                                 $tmparr = array_slice($chars, $j, ($sep + $endspace));
04117                                 if ($rtlmode) {
04118                                     $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
04119                                 }
04120                                 $linew = $this->GetArrStringWidth($tmparr);
04121                                 unset($tmparr);
04122                                 if ($this->rtl) {
04123                                     $this->endlinex = $startx - $linew - $shy_width;
04124                                 } else {
04125                                     $this->endlinex = $startx + $linew + $shy_width;
04126                                 }
04127                                 $w = $linew;
04128                                 $tmpcmargin = $this->cMargin;
04129                                 if ($maxh == 0) {
04130                                     $this->cMargin = 0;
04131                                 }
04132                             }
04133                             // print the line
04134                             $this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch);
04135                             unset($tmpstr);
04136                             if ($firstline) {
04137                                 // return the remaining text
04138                                 $this->cMargin = $tmpcmargin;
04139                                 return ($this->UniArrSubString($uchars, ($sep + $endspace)));
04140                             }
04141                             $i = $sep;
04142                             $sep = -1;
04143                             $shy = false;
04144                             $j = ($i+1);
04145                         }
04146                         // account for margin changes
04147                         if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND (!$this->InFooter)) {
04148                             // AcceptPageBreak() may be overriden on extended classed to include margin changes
04149                             $this->AcceptPageBreak();
04150                         }
04151                         $w = $this->getRemainingWidth();
04152                         $wmax = $w - (2 * $this->cMargin);
04153                         if ($linebreak) {
04154                             $linebreak = false;
04155                         } else {
04156                             ++$nl;
04157                             $l = 0;
04158                         }
04159                     }
04160                 }
04161                 ++$i;
04162             } // end while i < nb
04163             // print last substring (if any)
04164             if ($l > 0) {
04165                 switch ($align) {
04166                     case 'J':
04167                     case 'C': {
04168                         $w = $w;
04169                         break;
04170                     }
04171                     case 'L': {
04172                         if ($this->rtl) {
04173                             $w = $w;
04174                         } else {
04175                             $w = $l;
04176                         }
04177                         break;
04178                     }
04179                     case 'R': {
04180                         if ($this->rtl) {
04181                             $w = $l;
04182                         } else {
04183                             $w = $w;
04184                         }
04185                         break;
04186                     }
04187                     default: {
04188                         $w = $l;
04189                         break;
04190                     }
04191                 }
04192                 $tmpstr = $this->UniArrSubString($uchars, $j, $nb);
04193                 if ($firstline) {
04194                     $startx = $this->x;
04195                     $tmparr = array_slice($chars, $j, $nb);
04196                     if ($rtlmode) {
04197                         $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
04198                     }
04199                     $linew = $this->GetArrStringWidth($tmparr);
04200                     unset($tmparr);
04201                     if ($this->rtl) {
04202                         $this->endlinex = $startx - $linew;
04203                     } else {
04204                         $this->endlinex = $startx + $linew;
04205                     }
04206                     $w = $linew;
04207                     $tmpcmargin = $this->cMargin;
04208                     if ($maxh == 0) {
04209                         $this->cMargin = 0;
04210                     }
04211                 }
04212                 $this->Cell($w, $h, $tmpstr, 0, $ln, $align, $fill, $link, $stretch);
04213                 unset($tmpstr);
04214                 if ($firstline) {
04215                     $this->cMargin = $tmpcmargin;
04216                     return ($this->UniArrSubString($uchars, $nb));
04217                 }
04218                 ++$nl;
04219             }
04220             if ($firstline) {
04221                 return '';
04222             }
04223             return $nl;
04224         }
04225                 
04231         protected function getRemainingWidth() {
04232             if ($this->rtl) {
04233                 return ($this->x - $this->lMargin);
04234             } else {
04235                 return ($this->w - $this->rMargin - $this->x);
04236             }
04237         }
04238 
04247         public function UTF8ArrSubString($strarr, $start='', $end='') {
04248             if (strlen($start) == 0) {
04249                 $start = 0;
04250             }
04251             if (strlen($end) == 0) {
04252                 $end = count($strarr);
04253             }
04254             $string = '';
04255             for ($i=$start; $i < $end; ++$i) {
04256                 $string .= $this->unichr($strarr[$i]);
04257             }
04258             return $string;
04259         }
04260 
04270         public function UniArrSubString($uniarr, $start='', $end='') {
04271             if (strlen($start) == 0) {
04272                 $start = 0;
04273             }
04274             if (strlen($end) == 0) {
04275                 $end = count($uniarr);
04276             }
04277             $string = '';
04278             for ($i=$start; $i < $end; ++$i) {
04279                 $string .= $uniarr[$i];
04280             }
04281             return $string;
04282         }
04283 
04291         public function UTF8ArrayToUniArray($ta) {
04292             return array_map(array($this, 'unichr'), $ta);
04293         }
04294         
04303         public function unichr($c) {
04304             if (!$this->isunicode) {
04305                 return chr($c);
04306             } elseif ($c <= 0x7F) {
04307                 // one byte
04308                 return chr($c);
04309             } elseif ($c <= 0x7FF) {
04310                 // two bytes
04311                 return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F);
04312             } elseif ($c <= 0xFFFF) {
04313                 // three bytes
04314                 return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
04315             } elseif ($c <= 0x10FFFF) {
04316                 // four bytes
04317                 return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
04318             } else {
04319                 return '';
04320             }
04321         }
04322         
04352         public function Image($file, $x='', $y='', $w=0, $h=0, $type='', $link='', $align='', $resize=false, $dpi=300, $palign='', $ismask=false, $imgmask=false, $border=0) {
04353             if ($x === '') {
04354                 $x = $this->x;
04355             }
04356             if ($y === '') {
04357                 $y = $this->y;
04358             }
04359             // get image dimensions
04360             $imsize = @getimagesize($file);
04361             if ($imsize === FALSE) {
04362                 // encode spaces on filename
04363                 $file = str_replace(' ', '%20', $file);
04364                 $imsize = @getimagesize($file);
04365                 if ($imsize === FALSE) {
04366                     $this->Error('[Image] No such file or directory in '.$file);
04367                 }
04368             }
04369             // get original image width and height in pixels
04370             list($pixw, $pixh) = $imsize;
04371             // calculate image width and height on document
04372             if (($w <= 0) AND ($h <= 0)) {
04373                 // convert image size to document unit
04374                 $w = $this->pixelsToUnits($pixw);
04375                 $h = $this->pixelsToUnits($pixh);
04376             } elseif ($w <= 0) {
04377                 $w = $h * $pixw / $pixh;
04378             } elseif ($h <= 0) {
04379                 $h = $w * $pixh / $pixw;
04380             }
04381             // calculate new minimum dimensions in pixels
04382             $neww = round($w * $this->k * $dpi / $this->dpi);
04383             $newh = round($h * $this->k * $dpi / $this->dpi);
04384             // check if resize is necessary (resize is used only to reduce the image)
04385             if (($neww * $newh) >= ($pixw * $pixh)) {
04386                 $resize = false;
04387             }
04388             // check if image has been already added on document
04389             if (!in_array($file, $this->imagekeys)) {
04390                 //First use of image, get info
04391                 if ($type == '') {
04392                     $fileinfo = pathinfo($file);
04393                     if (isset($fileinfo['extension']) AND (!$this->empty_string($fileinfo['extension']))) {
04394                         $type = $fileinfo['extension'];
04395                     } else {
04396                         $this->Error('Image file has no extension and no type was specified: '.$file);
04397                     }
04398                 }
04399                 $type = strtolower($type);
04400                 if ($type == 'jpg') {
04401                     $type = 'jpeg';
04402                 }
04403                 $mqr = get_magic_quotes_runtime();
04404                 set_magic_quotes_runtime(0);
04405                 // Specific image handlers
04406                 $mtd = '_parse'.$type;
04407                 // GD image handler function
04408                 $gdfunction = 'imagecreatefrom'.$type;
04409                 $info = false;
04410                 if ((method_exists($this, $mtd)) AND (!($resize AND function_exists($gdfunction)))) {
04411                     // TCPDF image functions
04412                     $info = $this->$mtd($file);
04413                     if ($info == 'pngalpha') {
04414                         return $this->ImagePngAlpha($file, $x, $y, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign);
04415                     }
04416                 } 
04417                 if (!$info) {
04418                     if (function_exists($gdfunction)) {
04419                         // GD library
04420                         $img = $gdfunction($file);
04421                         if ($resize) {
04422                             $imgr = imagecreatetruecolor($neww, $newh);
04423                             imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh); 
04424                             $info = $this->_toJPEG($imgr);
04425                         } else {
04426                             $info = $this->_toJPEG($img);
04427                         }
04428                     } elseif (extension_loaded('imagick')) {
04429                         // ImageMagick library
04430                         $img = new Imagick();
04431                         $img->readImage($file);
04432                         if ($resize) {
04433                             $img->resizeImage($neww, $newh, 10, 1, false);
04434                         }
04435                         $img->setCompressionQuality($this->jpeg_quality);
04436                         $img->setImageFormat('jpeg');
04437                         $tempname = tempnam(K_PATH_CACHE, 'jpg_');
04438                         $img->writeImage($tempname);
04439                         $info = $this->_parsejpeg($tempname);
04440                         unlink($tempname);
04441                         $img->destroy();
04442                     } else {
04443                         return;
04444                     }
04445                 }
04446                 if ($info === false) {
04447                     //If false, we cannot process image
04448                     return;
04449                 }
04450                 set_magic_quotes_runtime($mqr);
04451                 if ($ismask) {
04452                     // force grayscale
04453                     $info['cs'] = 'DeviceGray';
04454                 }
04455                 $info['i'] = $this->numimages + 1;
04456                 if ($imgmask !== false) {
04457                     $info['masked'] = $imgmask;
04458                 }
04459                 // add image to document
04460                 $this->setImageBuffer($file, $info);
04461             } else {
04462                 $info = $this->getImageBuffer($file);
04463             }
04464             // Check whether we need a new page first as this does not fit
04465             if ((($y + $h) > $this->PageBreakTrigger) AND (!$this->InFooter) AND $this->AcceptPageBreak()) {
04466                 // Automatic page break
04467                 $this->AddPage($this->CurOrientation);
04468                 // Reset Y coordinate to the top of next page
04469                 $y = $this->GetY() + $this->cMargin;
04470             }
04471             // set bottomcoordinates
04472             $this->img_rb_y = $y + $h;
04473             // set alignment
04474             if ($this->rtl) {
04475                 if ($palign == 'L') {
04476                     $ximg = $this->lMargin;
04477                     // set right side coordinate
04478                     $this->img_rb_x = $ximg + $w;
04479                 } elseif ($palign == 'C') {
04480                     $ximg = ($this->w - $x - $w) / 2;
04481                     // set right side coordinate
04482                     $this->img_rb_x = $ximg + $w;
04483                 } else {
04484                     $ximg = $this->w - $x - $w;
04485                     // set left side coordinate
04486                     $this->img_rb_x = $ximg;
04487                 }
04488             } else {
04489                 if ($palign == 'R') {
04490                     $ximg = $this->w - $this->rMargin - $w;
04491                     // set left side coordinate
04492                     $this->img_rb_x = $ximg;
04493                 } elseif ($palign == 'C') {
04494                     $ximg = ($this->w - $x - $w) / 2;
04495                     // set right side coordinate
04496                     $this->img_rb_x = $ximg + $w;
04497                 } else {
04498                     $ximg = $x;
04499                     // set right side coordinate
04500                     $this->img_rb_x = $ximg + $w;
04501                 }
04502             }
04503             if ($ismask) {
04504                 // embed hidden, ouside the canvas
04505                 $xkimg = ($this->pagedim[$this->page]['w'] + 10);
04506             } else {
04507                 $xkimg = $ximg * $this->k;
04508             }
04509             $this->_out(sprintf('q %.2F 0 0 %.2F %.2F %.2F cm /I%d Do Q', ($w * $this->k), ($h * $this->k), $xkimg, (($this->h - ($y + $h)) * $this->k), $info['i']));
04510             if (!empty($border)) {
04511                 $bx = $x;
04512                 $by = $y;
04513                 $this->x = $ximg;
04514                 $this->y = $y;
04515                 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0);
04516                 $this->x = $bx;
04517                 $this->y = $by;
04518             }
04519             if ($link) {
04520                 $this->Link($ximg, $y, $w, $h, $link, 0);
04521             }
04522             // set pointer to align the successive text/objects
04523             switch($align) {
04524                 case 'T': {
04525                     $this->y = $y;
04526                     $this->x = $this->img_rb_x;
04527                     break;
04528                 }
04529                 case 'M': {
04530                     $this->y = $y + round($h/2);
04531                     $this->x = $this->img_rb_x;
04532                     break;
04533                 }
04534                 case 'B': {
04535                     $this->y = $this->img_rb_y;
04536                     $this->x = $this->img_rb_x;
04537                     break;
04538                 }
04539                 case 'N': {
04540                     $this->SetY($this->img_rb_y);
04541                     break;
04542                 }
04543                 default:{
04544                     break;
04545                 }
04546             }
04547             $this->endlinex = $this->img_rb_x;
04548             return $info['i'];
04549         }
04550                 
04559         protected function _toJPEG($image) {
04560             $tempname = tempnam(K_PATH_CACHE, 'jpg_');
04561             imagejpeg($image, $tempname, $this->jpeg_quality);
04562             imagedestroy($image);
04563             $retvars = $this->_parsejpeg($tempname);
04564             // tidy up by removing temporary image
04565             unlink($tempname);
04566             return $retvars;
04567         }
04568         
04575         protected function _parsejpeg($file) {
04576             $a = getimagesize($file);
04577             if (empty($a)) {
04578                 $this->Error('Missing or incorrect image file: '.$file);
04579             }
04580             if ($a[2] != 2) {
04581                 $this->Error('Not a JPEG file: '.$file);
04582             }
04583             if ((!isset($a['channels'])) OR ($a['channels'] == 3)) {
04584                 $colspace = 'DeviceRGB';
04585             } elseif ($a['channels'] == 4) {
04586                 $colspace = 'DeviceCMYK';
04587             } else {
04588                 $colspace = 'DeviceGray';
04589             }
04590             $bpc = isset($a['bits']) ? $a['bits'] : 8;
04591             $data = file_get_contents($file);
04592             return array('w' => $a[0], 'h' => $a[1], 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data);
04593         }
04594 
04601         protected function _parsepng($file) {
04602             $f = fopen($file, 'rb');
04603             if ($f === false) {
04604                 $this->Error('Can\'t open image file: '.$file);
04605             }
04606             //Check signature
04607             if (fread($f, 8) != chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) {
04608                 $this->Error('Not a PNG file: '.$file);
04609             }
04610             //Read header chunk
04611             fread($f, 4);
04612             if (fread($f, 4) != 'IHDR') {
04613                 $this->Error('Incorrect PNG file: '.$file);
04614             }
04615             $w = $this->_freadint($f);
04616             $h = $this->_freadint($f);
04617             $bpc = ord(fread($f, 1));
04618             if ($bpc > 8) {
04619                 //$this->Error('16-bit depth not supported: '.$file);
04620                 fclose($f);
04621                 return false;
04622             }
04623             $ct = ord(fread($f, 1));
04624             if ($ct == 0) {
04625                 $colspace = 'DeviceGray';
04626             } elseif ($ct == 2) {
04627                 $colspace = 'DeviceRGB';
04628             } elseif ($ct == 3) {
04629                 $colspace = 'Indexed';
04630             } else {
04631                 // alpha channel
04632                 fclose($f);
04633                 return 'pngalpha';
04634             }
04635             if (ord(fread($f, 1)) != 0) {
04636                 //$this->Error('Unknown compression method: '.$file);
04637                 fclose($f);
04638                 return false;
04639             }
04640             if (ord(fread($f, 1)) != 0) {
04641                 //$this->Error('Unknown filter method: '.$file);
04642                 fclose($f);
04643                 return false;
04644             }
04645             if (ord(fread($f, 1)) != 0) {
04646                 //$this->Error('Interlacing not supported: '.$file);
04647                 fclose($f);
04648                 return false;
04649             }
04650             fread($f, 4);
04651             $parms = '/DecodeParms <</Predictor 15 /Colors '.($ct==2 ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w.'>>';
04652             //Scan chunks looking for palette, transparency and image data
04653             $pal = '';
04654             $trns = '';
04655             $data = '';
04656             do {
04657                 $n = $this->_freadint($f);
04658                 $type = fread($f, 4);
04659                 if ($type == 'PLTE') {
04660                     //Read palette
04661                     $pal = $this->rfread($f, $n);
04662                     fread($f, 4);
04663                 } elseif ($type == 'tRNS') {
04664                     //Read transparency info
04665                     $t = $this->rfread($f, $n);
04666                     if ($ct == 0) {
04667                         $trns = array(ord(substr($t, 1, 1)));
04668                     } elseif ($ct == 2) {
04669                         $trns = array(ord(substr($t, 1, 1)), ord(substr($t, 3, 1)), ord(substr($t, 5, 1)));
04670                     } else {
04671                         $pos = strpos($t, chr(0));
04672                         if ($pos !== false) {
04673                             $trns = array($pos);
04674                         }
04675                     }
04676                     fread($f, 4);
04677                 } elseif ($type == 'IDAT') {
04678                     //Read image data block
04679                     $data .= $this->rfread($f, $n);
04680                     fread($f, 4);
04681                 } elseif ($type == 'IEND') {
04682                     break;
04683                 } else {
04684                     $this->rfread($f, $n + 4);
04685                 }
04686             } while ($n);
04687             if (($colspace == 'Indexed') AND (empty($pal))) {
04688                 //$this->Error('Missing palette in '.$file);
04689                 fclose($f);
04690                 return false;
04691             }
04692             fclose($f);
04693             return array('w' => $w, 'h' => $h, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'parms' => $parms, 'pal' => $pal, 'trns' => $trns, 'data' => $data);
04694         }
04695 
04706         protected function rfread($handle, $length) {
04707             $data = fread($handle, $length);
04708             if ($data === false) {
04709                 return false;
04710             }
04711             $rest = $length - strlen($data);
04712             if ($rest > 0) {
04713                 $data .= $this->rfread($handle, $rest);
04714             }
04715             return $data;
04716         }
04717 
04736         protected function ImagePngAlpha($file, $x='', $y='', $w=0, $h=0, $type='', $link='', $align='', $resize=false, $dpi=300, $palign='') {
04737             // get image size
04738             list($wpx, $hpx) = getimagesize($file);
04739             // generate images
04740             $img = imagecreatefrompng($file);
04741             $imgalpha = imagecreate($wpx, $hpx);
04742             // generate gray scale pallete
04743             for($c = 0; $c < 256; ++$c) {
04744                 ImageColorAllocate($imgalpha, $c, $c, $c);
04745             }
04746             // extract alpha channel
04747             for ($xpx = 0; $xpx < $wpx; ++$xpx) {
04748                 for ($ypx = 0; $ypx < $hpx; ++$ypx) {
04749                     $colorindex = imagecolorat($img, $xpx, $ypx);
04750                     $col = imagecolorsforindex($img, $colorindex);
04751                     imagesetpixel($imgalpha, $xpx, $ypx, $this->getGDgamma((127 - $col['alpha']) * 255 / 127));
04752                 }
04753             }
04754             // create temp alpha file
04755             $tempfile_alpha = tempnam(K_PATH_CACHE, 'mska_');
04756             imagepng($imgalpha, $tempfile_alpha);
04757             imagedestroy($imgalpha);
04758             // extract image without alpha channel
04759             $imgplain = imagecreatetruecolor($wpx, $hpx);
04760             imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
04761             // create temp image file
04762             $tempfile_plain = tempnam(K_PATH_CACHE, 'mskp_');
04763             imagepng($imgplain, $tempfile_plain);
04764             imagedestroy($imgplain);
04765             // embed mask image
04766             $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
04767             // embed image, masked with previously embedded mask
04768             $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
04769             // remove temp files
04770             unlink($tempfile_alpha);
04771             unlink($tempfile_plain);
04772         }
04773 
04780         protected function getGDgamma($v) {
04781             return (pow(($v / 255), 2.2) * 255);
04782         } 
04783         
04793         public function Ln($h='', $cell=false) {
04794             //Line feed; default value is last cell height
04795             if ($cell) {
04796                 $cellmargin = $this->cMargin;
04797             } else {
04798                 $cellmargin = 0;
04799             }
04800             if ($this->rtl) {
04801                 $this->x = $this->w - $this->rMargin - $cellmargin;
04802             } else {
04803                 $this->x = $this->lMargin + $cellmargin;
04804             }
04805             if (is_string($h)) {
04806                 $this->y += $this->lasth;
04807             } else {
04808                 $this->y += $h;
04809             }
04810             $this->newline = true;
04811         }
04812 
04821         public function GetX() {
04822             //Get x position
04823             if ($this->rtl) {
04824                 return ($this->w - $this->x);
04825             } else {
04826                 return $this->x;
04827             }
04828         }
04829         
04837         public function GetAbsX() {
04838             return $this->x;
04839         }
04840         
04848         public function GetY() {
04849             //Get y position
04850             return $this->y;
04851         }
04852         
04861         public function SetX($x) {
04862             //Set x position
04863             if ($this->rtl) {
04864                 if ($x >= 0) {
04865                     $this->x = $this->w - $x;
04866                 } else {
04867                     $this->x = abs($x);
04868                 }
04869             } else {
04870                 if ($x >= 0) {
04871                     $this->x = $x;
04872                 } else {
04873                     $this->x = $this->w + $x;
04874                 }
04875             }
04876             if ($this->x < 0) {
04877                 $this->x = 0;
04878             }
04879             if ($this->x > $this->w) {
04880                 $this->x = $this->w;
04881             }
04882         }
04883         
04893         public function SetY($y, $resetx=true) {
04894             if ($resetx) {
04895                 //reset x
04896                 if ($this->rtl) {
04897                     $this->x = $this->w - $this->rMargin;
04898                 } else {
04899                     $this->x = $this->lMargin;
04900                 }
04901             }
04902             if ($y >= 0) {
04903                 $this->y = $y;
04904             } else {
04905                 $this->y = $this->h + $y;
04906             }
04907             if ($this->y < 0) {
04908                 $this->y = 0;
04909             }
04910             if ($this->y > $this->h) {
04911                 $this->y = $this->h;
04912             }
04913         }
04914         
04924         public function SetXY($x, $y) {
04925             //Set x and y positions
04926             $this->SetY($y);
04927             $this->SetX($x);
04928         }
04929 
04940         public function Output($name='doc.pdf', $dest='I') {
04941             //Output PDF to some destination
04942             //Finish document if necessary
04943             if ($this->state < 3) {
04944                 $this->Close();
04945             }
04946             //Normalize parameters
04947             if (is_bool($dest)) {
04948                 $dest = $dest ? 'D' : 'F';
04949             }
04950             $dest = strtoupper($dest);
04951             if ($dest != 'F') {
04952                 $name = preg_replace('/[\s]+/', '_', $name);
04953                 $name = preg_replace('/[^a-zA-Z0-9_\.-]/', '', $name);
04954             }
04955             if ($this->sign) {
04956                 // *** apply digital signature to the document ***
04957                 // get the document content
04958                 $pdfdoc = $this->getBuffer();
04959                 // remove last newline
04960                 $pdfdoc = substr($pdfdoc, 0, -1);
04961                 // Remove the original buffer
04962                 if (isset($this->diskcache) AND $this->diskcache) {
04963                     // remove buffer file from cache
04964                     unlink($this->buffer);
04965                 }
04966                 unset($this->buffer);
04967                 // remove filler space
04968                 $tmppos = strpos($pdfdoc, '/ByteRange[0 ********** ********** **********]') + 58;
04969                 $pdfdoc = substr($pdfdoc, 0, $tmppos).substr($pdfdoc, $tmppos + $this->signature_max_lenght);
04970                 // define the ByteRange
04971                 $byte_range = array();
04972                 $byte_range[0] = 0;
04973                 $byte_range[1] = $tmppos - 1;
04974                 $byte_range[2] = $byte_range[1] + $this->signature_max_lenght;
04975                 $byte_range[3] = strlen($pdfdoc) - $byte_range[1];
04976                 // replace the ByteRange
04977                 $byterange = sprintf('/ByteRange[0 %010u %010u %010u]', $byte_range[1], $byte_range[2], $byte_range[3]);
04978                 $pdfdoc = str_replace('/ByteRange[0 ********** ********** **********]', $byterange, $pdfdoc);
04979                 // write the document to a temporary folder
04980                 $tempdoc = tempnam(K_PATH_CACHE, 'tmppdf_');
04981                 $f = fopen($tempdoc, 'wb');
04982                 if (!$f) {
04983                     $this->Error('Unable to create temporary file: '.$tempdoc);
04984                 }
04985                 $pdfdoc_lenght = strlen($pdfdoc);
04986                 fwrite($f, $pdfdoc, $pdfdoc_lenght);
04987                 fclose($f);
04988                 // get digital signature.
04989                 // IS THE FOLLOWING PROCEDURE CORRECT? THE SIGNED DOCUMENTS ARE NOT VALID!
04990                 $tempsign = tempnam(K_PATH_CACHE, 'tmpsig_');
04991                 if (empty($this->signature_data['extracerts'])) {
04992                     openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED);
04993                 } else {
04994                     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']);
04995                 }   
04996                 unlink($tempdoc);
04997                 // read signature
04998                 $signature = file_get_contents($tempsign, false, null, $pdfdoc_lenght);
04999                 unlink($tempsign);
05000                 // extract signature
05001                 $signature = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13));
05002                 $tmparr = explode("\n\n", $signature);
05003                 $signature = $tmparr[1];
05004                 unset($tmparr);
05005                 // decode signature
05006                 $signature = base64_decode(trim($signature));
05007                 // convert signature to hex
05008                 $signature = current(unpack('H*', $signature));
05009                 $signature = str_pad($signature, $this->signature_max_lenght, '0');
05010                 // Add signature to the document
05011                 $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).$signature.substr($pdfdoc, (0 - $byte_range[3]));
05012                 $this->diskcache = false;
05013                 $this->buffer = &$pdfdoc;
05014                 $this->bufferlen = strlen($pdfdoc);
05015             }
05016             switch($dest) {
05017                 case 'I': {
05018                     // Send PDF to the standard output
05019                     if (ob_get_contents()) {
05020                         $this->Error('Some data has already been output, can\'t send PDF file');
05021                     }
05022                     if (php_sapi_name() != 'cli') {
05023                         //We send to a browser
05024                         header('Content-Type: application/pdf');
05025                         if (headers_sent()) {
05026                             $this->Error('Some data has already been output to browser, can\'t send PDF file');
05027                         }
05028                         header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
05029                         header('Pragma: public');
05030                         header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
05031                         header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');  
05032                         header('Content-Length: '.$this->bufferlen);
05033                         header('Content-Disposition: inline; filename="'.basename($name).'";');
05034                     }
05035                     echo $this->getBuffer();
05036                     break;
05037                 }
05038                 case 'D': {
05039                     // Download PDF as file
05040                     if (ob_get_contents()) {
05041                         $this->Error('Some data has already been output, can\'t send PDF file');
05042                     }
05043                     header('Content-Description: File Transfer');
05044                     if (headers_sent()) {
05045                         $this->Error('Some data has already been output to browser, can\'t send PDF file');
05046                     }
05047                     header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
05048                     header('Pragma: public');
05049                     header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
05050                     header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
05051                     // force download dialog
05052                     header('Content-Type: application/force-download');
05053                     header('Content-Type: application/octet-stream', false);
05054                     header('Content-Type: application/download', false);
05055                     header('Content-Type: application/pdf', false);
05056                     // use the Content-Disposition header to supply a recommended filename
05057                     header('Content-Disposition: attachment; filename="'.basename($name).'";');
05058                     header('Content-Transfer-Encoding: binary');
05059                     header('Content-Length: '.$this->bufferlen);
05060                     echo $this->getBuffer();
05061                     break;
05062                 }
05063                 case 'F': {
05064                     // Save PDF to a local file
05065                     if ($this->diskcache) {
05066                         copy($this->buffer, $name);
05067                     } else {
05068                         $f = fopen($name, 'wb');
05069                         if (!$f) {
05070                             $this->Error('Unable to create output file: '.$name);
05071                         }
05072                         fwrite($f, $this->getBuffer(), $this->bufferlen);
05073                         fclose($f);
05074                     }
05075                     break;
05076                 }
05077                 case 'S': {
05078                     // Returns PDF as a string
05079                     return $this->getBuffer();
05080                 }
05081                 default: {
05082                     $this->Error('Incorrect output destination: '.$dest);
05083                 }
05084             }
05085             return '';
05086         }
05087 
05095         public function _destroy($destroyall=false, $preserve_objcopy=false) {
05096             if ($destroyall AND isset($this->diskcache) AND $this->diskcache AND (!$preserve_objcopy) AND (!$this->empty_string($this->buffer))) {
05097                 // remove buffer file from cache
05098                 unlink($this->buffer);
05099             }
05100             foreach (array_keys(get_object_vars($this)) as $val) {
05101                 if ($destroyall OR (
05102                     ($val != 'internal_encoding') 
05103                     AND ($val != 'state') 
05104                     AND ($val != 'bufferlen') 
05105                     AND ($val != 'buffer') 
05106                     AND ($val != 'diskcache')
05107                     AND ($val != 'sign')
05108                     AND ($val != 'signature_data')
05109                     AND ($val != 'signature_max_lenght')
05110                     )) {
05111                     if (!$preserve_objcopy OR ($val != 'objcopy')) {
05112                         unset($this->$val);
05113                     }
05114                 }
05115             }
05116         }
05117         
05122         protected function _dochecks() {
05123             //Check for locale-related bug
05124             if (1.1 == 1) {
05125                 $this->Error('Don\'t alter the locale before including class file');
05126             }
05127             //Check for decimal separator
05128             if (sprintf('%.1F', 1.0) != '1.0') {
05129                 setlocale(LC_NUMERIC, 'C');
05130             }
05131         }
05132 
05138         protected function _getfontpath() {
05139             if (!defined('K_PATH_FONTS') AND is_dir(dirname(__FILE__).'/fonts')) {
05140                 define('K_PATH_FONTS', dirname(__FILE__).'/fonts/');
05141             }
05142             return defined('K_PATH_FONTS') ? K_PATH_FONTS : '';
05143         }
05144         
05149         protected function _putpages() {
05150             $nb = $this->numpages;
05151             if (!empty($this->AliasNbPages)) {
05152                 $nbs = $this->formatPageNumber($nb);
05153                 $nbu = $this->UTF8ToUTF16BE($nbs, false); // replacement for unicode font
05154                 $alias_a = $this->_escape($this->AliasNbPages);
05155                 $alias_au = $this->_escape('{'.$this->AliasNbPages.'}');
05156                 if ($this->isunicode) {
05157                     $alias_b = $this->_escape($this->UTF8ToLatin1($this->AliasNbPages));
05158                     $alias_bu = $this->_escape($this->UTF8ToLatin1('{'.$this->AliasNbPages.'}'));
05159                     $alias_c = $this->_escape($this->utf8StrRev($this->AliasNbPages, false, $this->tmprtl));
05160                     $alias_cu = $this->_escape($this->utf8StrRev('{'.$this->AliasNbPages.'}', false, $this->tmprtl));
05161                 }
05162             }
05163             if (!empty($this->AliasNumPage)) {
05164                 $alias_pa = $this->_escape($this->AliasNumPage);
05165                 $alias_pau = $this->_escape('{'.$this->AliasNumPage.'}');
05166                 if ($this->isunicode) {
05167                     $alias_pb = $this->_escape($this->UTF8ToLatin1($this->AliasNumPage));
05168                     $alias_pbu = $this->_escape($this->UTF8ToLatin1('{'.$this->AliasNumPage.'}'));
05169                     $alias_pc = $this->_escape($this->utf8StrRev($this->AliasNumPage, false, $this->tmprtl));
05170                     $alias_pcu = $this->_escape($this->utf8StrRev('{'.$this->AliasNumPage.'}', false, $this->tmprtl));
05171                 }
05172             }
05173             $pagegroupnum = 0;
05174             $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
05175             for($n=1; $n <= $nb; ++$n) {
05176                 $temppage = $this->getPageBuffer($n);
05177                 if (!empty($this->pagegroups)) {
05178                     if(isset($this->newpagegroup[$n])) {
05179                         $pagegroupnum = 0;
05180                     }
05181                     ++$pagegroupnum;
05182                     foreach ($this->pagegroups as $k => $v) {
05183                         // replace total pages group numbers
05184                         $vs = $this->formatPageNumber($v);
05185                         $vu = $this->UTF8ToUTF16BE($vs, false);
05186                         $alias_ga = $this->_escape($k);
05187                         $alias_gau = $this->_escape('{'.$k.'}');
05188                         if ($this->isunicode) {
05189                             $alias_gb = $this->_escape($this->UTF8ToLatin1($k));
05190                             $alias_gbu = $this->_escape($this->UTF8ToLatin1('{'.$k.'}'));
05191                             $alias_gc = $this->_escape($this->utf8StrRev($k, false, $this->tmprtl));
05192                             $alias_gcu = $this->_escape($this->utf8StrRev('{'.$k.'}', false, $this->tmprtl));
05193                         }
05194                         $temppage = str_replace($alias_gau, $vu, $temppage);
05195                         if ($this->isunicode) {
05196                             $temppage = str_replace($alias_gbu, $vu, $temppage);
05197                             $temppage = str_replace($alias_gcu, $vu, $temppage);
05198                             $temppage = str_replace($alias_gb, $vs, $temppage);
05199                             $temppage = str_replace($alias_gc, $vs, $temppage);
05200                         }
05201                         $temppage = str_replace($alias_ga, $vs, $temppage);
05202                         // replace page group numbers
05203                         $pvs = $this->formatPageNumber($pagegroupnum);
05204                         $pvu = $this->UTF8ToUTF16BE($pvs, false);
05205                         $pk = str_replace('{nb', '{pnb', $k);
05206                         $alias_pga = $this->_escape($pk);
05207                         $alias_pgau = $this->_escape('{'.$pk.'}');
05208                         if ($this->isunicode) {
05209                             $alias_pgb = $this->_escape($this->UTF8ToLatin1($pk));
05210                             $alias_pgbu = $this->_escape($this->UTF8ToLatin1('{'.$pk.'}'));
05211                             $alias_pgc = $this->_escape($this->utf8StrRev($pk, false, $this->tmprtl));
05212                             $alias_pgcu = $this->_escape($this->utf8StrRev('{'.$pk.'}', false, $this->tmprtl));
05213                         }
05214                         $temppage = str_replace($alias_pgau, $pvu, $temppage);
05215                         if ($this->isunicode) {
05216                             $temppage = str_replace($alias_pgbu, $pvu, $temppage);
05217                             $temppage = str_replace($alias_pgcu, $pvu, $temppage);
05218                             $temppage = str_replace($alias_pgb, $pvs, $temppage);
05219                             $temppage = str_replace($alias_pgc, $pvs, $temppage);
05220                         }
05221                         $temppage = str_replace($alias_pga, $pvs, $temppage);
05222                     }
05223                 }
05224                 if (!empty($this->AliasNbPages)) {
05225                     // replace total pages number
05226                     $temppage = str_replace($alias_au, $nbu, $temppage);
05227                     if ($this->isunicode) {
05228                         $temppage = str_replace($alias_bu, $nbu, $temppage);
05229                         $temppage = str_replace($alias_cu, $nbu, $temppage);
05230                         $temppage = str_replace($alias_b, $nbs, $temppage);
05231                         $temppage = str_replace($alias_c, $nbs, $temppage);
05232                     }
05233                     $temppage = str_replace($alias_a, $nbs, $temppage);
05234                 }
05235                 if (!empty($this->AliasNumPage)) {
05236                     // replace page number
05237                     $pnbs = $this->formatPageNumber($n);
05238                     $pnbu = $this->UTF8ToUTF16BE($pnbs, false); // replacement for unicode font
05239                     $temppage = str_replace($alias_pau, $pnbu, $temppage);
05240                     if ($this->isunicode) {
05241                         $temppage = str_replace($alias_pbu, $pnbu, $temppage);
05242                         $temppage = str_replace($alias_pcu, $pnbu, $temppage);
05243                         $temppage = str_replace($alias_pb, $pnbs, $temppage);
05244                         $temppage = str_replace($alias_pc, $pnbs, $temppage);
05245                     }
05246                     $temppage = str_replace($alias_pa, $pnbs, $temppage);
05247                 }
05248                 $temppage = str_replace($this->epsmarker, '', $temppage);
05249                 //$this->setPageBuffer($n, $temppage);
05250                 //Page
05251                 $this->_newobj();
05252                 $this->_out('<</Type /Page');
05253                 $this->_out('/Parent 1 0 R');
05254                 $this->_out(sprintf('/MediaBox [0 0 %.2F %.2F]', $this->pagedim[$n]['w'], $this->pagedim[$n]['h']));
05255                 $this->_out('/Resources 2 0 R');
05256                 $this->_putannots($n);
05257                 $this->_out('/Contents '.($this->n + 1).' 0 R>>');
05258                 $this->_out('endobj');
05259                 //Page content
05260                 $p = ($this->compress) ? gzcompress($temppage) : $temppage;
05261                 $this->_newobj();
05262                 $this->_out('<<'.$filter.'/Length '.strlen($p).'>>');
05263                 $this->_putstream($p);
05264                 $this->_out('endobj');
05265                 if ($this->diskcache) {
05266                     // remove temporary files
05267                     unlink($this->pages[$n]);
05268                 }
05269             }
05270             //Pages root
05271             $this->offsets[1] = $this->bufferlen;
05272             $this->_out('1 0 obj');
05273             $this->_out('<</Type /Pages');
05274             $kids='/Kids [';
05275             for($i=0; $i < $nb; ++$i) {
05276                 $kids .= (3 + (2 * $i)).' 0 R ';
05277             }
05278             $this->_out($kids.']');
05279             $this->_out('/Count '.$nb);
05280             //$this->_out(sprintf('/MediaBox [0 0 %.2F %.2F]',$this->pagedim[0]['w'],$this->pagedim[0]['h']));
05281             $this->_out('>>');
05282             $this->_out('endobj');
05283         }
05284 
05294         protected function _putannots($n) {
05295             if (isset($this->PageAnnots[$n])) {
05296                 $annots = '/Annots [';
05297                 foreach ($this->PageAnnots[$n] as $key => $pl) {
05298                     $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER);
05299                     $a = $pl['x'] * $this->k;
05300                     $b = $this->pagedim[$n]['h'] - ($pl['y']  * $this->k);
05301                     $c = $pl['w'] * $this->k;
05302                     $d = $pl['h'] * $this->k;
05303                     $rect = sprintf('%.2F %.2F %.2F %.2F', $a, $b, $a+$c, $b-$d);
05304                     $annots .= "\n";
05305                     $annots .= '<</Type /Annot';
05306                     $annots .= ' /Subtype /'.$pl['opt']['subtype'];
05307                     $annots .= ' /Rect ['.$rect.']';
05308                     $annots .= ' /Contents '.$this->_textstring($pl['txt']);
05309                     //$annots .= ' /P ';
05310                     $annots .= ' /NM '.$this->_textstring(sprintf('%04u-%04u', $n, $key));
05311                     $annots .= ' /M '.$this->_datastring('D:'.date('YmdHis'));
05312                     if (isset($pl['opt']['f'])) {
05313                         $val = 0;
05314                         if (is_array($pl['opt']['f'])) {
05315                             foreach ($pl['opt']['f'] as $f) {
05316                                 switch (strtolower($f)) {
05317                                     case 'invisible': {
05318                                         $val += 1 << 0;
05319                                         break;
05320                                     }
05321                                     case 'hidden': {
05322                                         $val += 1 << 1;
05323                                         break;
05324                                     }
05325                                     case 'print': {
05326                                         $val += 1 << 2;
05327                                         break;
05328                                     }
05329                                     case 'nozoom': {
05330                                         $val += 1 << 3;
05331                                         break;
05332                                     }
05333                                     case 'norotate': {
05334                                         $val += 1 << 4;
05335                                         break;
05336                                     }
05337                                     case 'noview': {
05338                                         $val += 1 << 5;
05339                                         break;
05340                                     }
05341                                     case 'readonly': {
05342                                         $val += 1 << 6;
05343                                         break;
05344                                     }
05345                                     case 'locked': {
05346                                         $val += 1 << 8;
05347                                         break;
05348                                     }
05349                                     case 'togglenoview': {
05350                                         $val += 1 << 9;
05351                                         break;
05352                                     }
05353                                     case 'lockedcontents': {
05354                                         $val += 1 << 10;
05355                                         break;
05356                                     }
05357                                     default: {
05358                                         break;
05359                                     }
05360                                 }
05361                             }
05362                         }
05363                         $annots .= ' /F '.intval($val);
05364                     }
05365                     //$annots .= ' /AP ';
05366                     //$annots .= ' /AS ';
05367                     $annots .= ' /Border [';
05368                     if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) {
05369                         $annots .= intval($pl['opt']['border'][0]).' ';
05370                         $annots .= intval($pl['opt']['border'][1]).' ';
05371                         $annots .= intval($pl['opt']['border'][2]);
05372                         if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) {
05373                             $annots .= ' [';
05374                             foreach ($pl['opt']['border'][3] as $dash) {
05375                                 $annots .= intval($dash).' ';
05376                             }
05377                             $annots .= ']';
05378                         }
05379                     } else {
05380                         $annots .= '0 0 0';
05381                     }
05382                     $annots .= ']';
05383                     if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) {
05384                         $annots .= ' /BS <<Type /Border';
05385                         if (isset($pl['opt']['bs']['w'])) {
05386                             $annots .= ' /W '.sprintf("%.4F", floatval($pl['opt']['bs']['w']));
05387                         }
05388                         $bstyles = array('S', 'D', 'B', 'I', 'U');
05389                         if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $bstyles)) {
05390                             $annots .= ' /S /'.$pl['opt']['bs']['s'];
05391                         }
05392                         if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) {
05393                             $annots .= ' /D [';
05394                             foreach ($pl['opt']['bs']['d'] as $cord) {
05395                                 $cord = floatval($cord);
05396                                 $annots .= sprintf(" %.4F", $cord);
05397                             }
05398                             $annots .= ']';
05399                         }
05400                         $annots .= '>> ';
05401                     }
05402                     if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) {
05403                         $annots .= ' /BE <<';
05404                         $bstyles = array('S', 'C');
05405                         if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $markups)) {
05406                             $annots .= ' /S /'.$pl['opt']['bs']['s'];
05407                         } else {
05408                             $annots .= ' /S /S';
05409                         }
05410                         if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) {
05411                             $annots .= ' /I '.sprintf(" %.4F", $pl['opt']['be']['i']);
05412                         }
05413                         $annots .= '>>';
05414                     }
05415                     $annots .= ' /C [';
05416                     if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c']))) {
05417                         foreach ($pl['opt']['c'] as $col) {
05418                             $col = intval($col);
05419                             $color = $col <= 0 ? 0 : ($col >= 255 ? 1 : $col / 255);
05420                             $annots .= sprintf(" %.4F", $color);
05421                         }
05422                     }
05423                     $annots .= ']';
05424                     //$annots .= ' /StructParent ';
05425                     //$annots .= ' /OC ';
05426                     $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight',  'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound');
05427                     if (in_array(strtolower($pl['opt']['subtype']), $markups)) {
05428                         // this is a markup type
05429                         if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
05430                             $annots .= ' /T '.$this->_textstring($pl['opt']['t']);
05431                         }
05432                         //$annots .= ' /Popup ';
05433                         if (isset($pl['opt']['ca'])) {
05434                             $annots .= ' /CA '.sprintf("%.4F", floatval($pl['opt']['ca']));
05435                         }
05436                         if (isset($pl['opt']['rc'])) {
05437                             $annots .= ' /RC '.$this->_textstring($pl['opt']['rc']);
05438                         }
05439                         $annots .= ' /CreationDate '.$this->_datastring('D:'.date('YmdHis'));
05440                         //$annots .= ' /IRT ';
05441                         if (isset($pl['opt']['subj'])) {
05442                             $annots .= ' /Subj '.$this->_textstring($pl['opt']['subj']);
05443                         }
05444                         //$annots .= ' /RT ';
05445                         //$annots .= ' /IT ';
05446                         //$annots .= ' /ExData ';
05447                     }
05448                     switch (strtolower($pl['opt']['subtype'])) {
05449                         case 'text': {
05450                             if (isset($pl['opt']['open'])) {
05451                                 $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false');
05452                             }
05453                             $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph');
05454                             if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
05455                                 $annots .= ' /Name /'.$pl['opt']['name'];
05456                             } else {
05457                                 $annots .= ' /Name /Note';
05458                             }
05459                             $statemodels = array('Marked', 'Review');
05460                             if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) {
05461                                 $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
05462                             } else {
05463                                 $pl['opt']['statemodel'] = 'Marked';
05464                                 $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
05465                             }
05466                             if ($pl['opt']['statemodel'] == 'Marked') {
05467                                 $states = array('Accepted', 'Unmarked');
05468                             } else {
05469                                 $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None');
05470                             }
05471                             if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) {
05472                                 $annots .= ' /State /'.$pl['opt']['state'];
05473                             } else {
05474                                 if ($pl['opt']['statemodel'] == 'Marked') {
05475                                     $annots .= ' /State /Unmarked';
05476                                 } else {
05477                                     $annots .= ' /State /None';
05478                                 }
05479                             }
05480                             break;
05481                         }
05482                         case 'link': {
05483                             if(is_string($pl['txt'])) {
05484                                 // external URI link
05485                                 $annots .= ' /A <</S /URI /URI '.$this->_datastring($pl['txt']).'>>';
05486                             } else {
05487                                 // internal link
05488                                 $l = $this->links[$pl['txt']];
05489                                 $annots .= sprintf(' /Dest [%d 0 R /XYZ 0 %.2F null]', (1 + (2 * $l[0])), ($this->pagedim[$l[0]]['h'] - ($l[1] * $this->k)));
05490                             }
05491                             $hmodes = array('N', 'I', 'O', 'P');
05492                             if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) {
05493                                 $annots .= ' /H /'.$pl['opt']['h'];
05494                             } else {
05495                                 $annots .= ' /H /I';
05496                             }
05497                             //$annots .= ' /PA ';
05498                             //$annots .= ' /Quadpoints ';
05499                             break;
05500                         }
05501                         case 'freetext': {
05502                             $annots .= ' /DA '.$this->_textstring($pl['txt']);
05503                             if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
05504                                 $annots .= ' /Q '.intval($pl['opt']['q']);
05505                             }
05506                             if (isset($pl['opt']['rc'])) {
05507                                 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc']);
05508                             }
05509                             if (isset($pl['opt']['ds'])) {
05510                                 $annots .= ' /DS '.$this->_textstring($pl['opt']['ds']);
05511                             }
05512                             if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) {
05513                                 $annots .= ' /CL [';
05514                                 foreach ($pl['opt']['cl'] as $cl) {
05515                                     $annots .= sprintf("%.4F ", $cl * $this->k);
05516                                 }
05517                                 $annots .= ']';
05518                             }
05519                             $tfit = array('FreeTextCallout', 'FreeTextTypeWriter');
05520                             if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) {
05521                                 $annots .= ' /IT '.$pl['opt']['it'];
05522                             }
05523                             if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) {
05524                                 $l = $pl['opt']['rd'][0] * $this->k;
05525                                 $r = $pl['opt']['rd'][1] * $this->k;
05526                                 $t = $pl['opt']['rd'][2] * $this->k;
05527                                 $b = $pl['opt']['rd'][3] * $this->k;
05528                                 $annots .= ' /RD ['.sprintf('%.2F %.2F %.2F %.2F', $l, $r, $t, $b).']';
05529                             }
05530                             //$annots .= ' /LE ';
05531                             break;
05532                         }
05533                         // ... to be completed ...
05534                         case 'line': {
05535                             break;
05536                         }
05537                         case 'square': {
05538                             break;
05539                         }
05540                         case 'circle': {
05541                             break;
05542                         }
05543                         case 'polygon': {
05544                             break;
05545                         }
05546                         case 'polyline': {
05547                             break;
05548                         }
05549                         case 'highlight': {
05550                             break;
05551                         }
05552                         case 'underline': {
05553                             break;
05554                         }
05555                         case 'squiggly': {
05556                             break;
05557                         }
05558                         case 'strikeout': {
05559                             break;
05560                         }
05561                         case 'stamp': {
05562                             break;
05563                         }
05564                         case 'caret': {
05565                             break;
05566                         }
05567                         case 'ink': {
05568                             break;
05569                         }
05570                         case 'popup': {
05571                             break;
05572                         }
05573                         case 'fileattachment': {
05574                             if (!isset($pl['opt']['fs'])) {
05575                                 break;
05576                             }
05577                             $filename = basename($pl['opt']['fs']);
05578                             if (isset($this->embeddedfiles[$filename]['n'])) {
05579                                 $annots .= ' /FS <</Type /Filespec /F '.$this->_datastring($filename).' /EF <</F '.$this->embeddedfiles[$filename]['n'].' 0 R>> >>';
05580                                 $iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag');
05581                                 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
05582                                     $annots .= ' /Name /'.$pl['opt']['name'];
05583                                 } else {
05584                                     $annots .= ' /Name /PushPin';
05585                                 }
05586                             }
05587                             break;
05588                         }
05589                         case 'sound': {
05590                             if (!isset($pl['opt']['sound'])) {
05591                                 break;
05592                             }
05593                             $filename = basename($pl['opt']['sound']);
05594                             if (isset($this->embeddedfiles[$filename]['n'])) {
05595                                 // ... TO BE COMPLETED ...
05596                                 $iconsapp = array('Speaker', 'Mic');
05597                                 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
05598                                     $annots .= ' /Name /'.$pl['opt']['name'];
05599                                 } else {
05600                                     $annots .= ' /Name /Speaker';
05601                                 }
05602                             }
05603                             break;
05604                         }
05605                         case 'movie': {
05606                             break;
05607                         }
05608                         case 'widget': {
05609                             if (isset($pl['opt']['h'])) {
05610                                 $annots .= ' /H '.intval($pl['opt']['h']);
05611                             }
05612                             if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk']))) {
05613                                 $annots .= ' /MK <<';
05614                                 // ... TO BE COMPLETED ...
05615                                 $annots .= '>>';
05616                             }
05617                             break;
05618                         }
05619                         case 'screen': {
05620                             break;
05621                         }
05622                         case 'printermark': {
05623                             break;
05624                         }
05625                         case 'trapnet': {
05626                             break;
05627                         }
05628                         case 'watermark': {
05629                             break;
05630                         }
05631                         case '3d': {
05632                             break;
05633                         }
05634                         default: {
05635                             break;
05636                         }
05637                     }
05638                     
05639                 $annots .= '>>';
05640                 }
05641                 $annots .= "\n]";
05642                 $this->_out($annots);
05643             }
05644         }
05645 
05650         protected function _putfonts() {
05651             $nf = $this->n;
05652             foreach ($this->diffs as $diff) {
05653                 //Encodings
05654                 $this->_newobj();
05655                 $this->_out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.']>>');
05656                 $this->_out('endobj');
05657             }
05658             $mqr = get_magic_quotes_runtime();
05659             set_magic_quotes_runtime(0);
05660             foreach ($this->FontFiles as $file => $info) {
05661                 // search and get font file to embedd
05662                 $fontdir = $info['fontdir'];
05663                 $file = strtolower($file);
05664                 $fontfile = '';
05665                 // search files on various directories
05666                 if (file_exists($fontdir.$file)) {
05667                     $fontfile = $fontdir.$file;
05668                 } elseif (file_exists($this->_getfontpath().$file)) {
05669                     $fontfile = $this->_getfontpath().$file;
05670                 } elseif (file_exists($file)) {
05671                     $fontfile = $file;
05672                 }
05673                 if (!$this->empty_string($fontfile)) {
05674                     $font = file_get_contents($fontfile);
05675                     $compressed = (substr($file, -2) == '.z');
05676                     if ((!$compressed) AND (isset($info['length2']))) {
05677                         $header = (ord($font{0}) == 128);
05678                         if ($header) {
05679                             //Strip first binary header
05680                             $font = substr($font, 6);
05681                         }
05682                         if ($header AND (ord($font{$info['length1']}) == 128)) {
05683                             //Strip second binary header
05684                             $font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] + 6));
05685                         }
05686                     }
05687                     $this->_newobj();
05688                     $this->FontFiles[$file]['n'] = $this->n;
05689                     $this->_out('<</Length '.strlen($font));
05690                     if ($compressed) {
05691                         $this->_out('/Filter /FlateDecode');
05692                     }
05693                     $this->_out('/Length1 '.$info['length1']);
05694                     if (isset($info['length2'])) {
05695                         $this->_out('/Length2 '.$info['length2'].' /Length3 0');
05696                     }
05697                     $this->_out('>>');
05698                     $this->_putstream($font);
05699                     $this->_out('endobj');
05700                 }
05701             }
05702             set_magic_quotes_runtime($mqr);
05703             foreach ($this->fontkeys as $k) {
05704                 //Font objects
05705                 $this->setFontSubBuffer($k, 'n', $this->n + 1);
05706                 $font = $this->getFontBuffer($k);
05707                 $type = $font['type'];
05708                 $name = $font['name'];
05709                 if ($type == 'core') {
05710                     //Standard font
05711                     $this->_newobj();
05712                     $this->_out('<</Type /Font');
05713                     $this->_out('/BaseFont /'.$name);
05714                     $this->_out('/Subtype /Type1');
05715                     if (($name != 'symbol') AND ($name != 'zapfdingbats')) {
05716                         $this->_out('/Encoding /WinAnsiEncoding');
05717                     }
05718                     $this->_out('>>');
05719                     $this->_out('endobj');
05720                 } elseif (($type == 'Type1') OR ($type == 'TrueType')) {
05721                     //Additional Type1 or TrueType font
05722                     $this->_newobj();
05723                     $this->_out('<</Type /Font');
05724                     $this->_out('/BaseFont /'.$name);
05725                     $this->_out('/Subtype /'.$type);
05726                     $this->_out('/FirstChar 32 /LastChar 255');
05727                     $this->_out('/Widths '.($this->n + 1).' 0 R');
05728                     $this->_out('/FontDescriptor '.($this->n + 2).' 0 R');
05729                     if ($font['enc']) {
05730                         if (isset($font['diff'])) {
05731                             $this->_out('/Encoding '.($nf + $font['diff']).' 0 R');
05732                         } else {
05733                             $this->_out('/Encoding /WinAnsiEncoding');
05734                         }
05735                     }
05736                     $this->_out('>>');
05737                     $this->_out('endobj');
05738                     // Widths
05739                     $this->_newobj();
05740                     $cw = &$font['cw'];
05741                     $s = '[';
05742                     for($i = 32; $i < 256; ++$i) {
05743                         $s .= $cw[$i].' ';
05744                     }
05745                     $this->_out($s.']');
05746                     $this->_out('endobj');
05747                     //Descriptor
05748                     $this->_newobj();
05749                     $s = '<</Type /FontDescriptor /FontName /'.$name;
05750                     foreach ($font['desc'] as $k => $v) {
05751                         $s .= ' /'.$k.' '.$v.'';
05752                     }
05753                     if (!$this->empty_string($font['file'])) {
05754                         $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R';
05755                     }
05756                     $this->_out($s.'>>');
05757                     $this->_out('endobj');
05758                 } else {
05759                     //Allow for additional types
05760                     $mtd = '_put'.strtolower($type);
05761                     if (!method_exists($this, $mtd)) {
05762                         $this->Error('Unsupported font type: '.$type);
05763                     }
05764                     $this->$mtd($font);
05765                 }
05766             }
05767         }
05768         
05777         protected function _putfontwidths($font, $cidoffset=0) {
05778             ksort($font['cw']);
05779             $rangeid = 0;
05780             $range = array();
05781             $prevcid = -2;
05782             $prevwidth = -1;
05783             $interval = false;
05784             // for each character
05785             foreach ($font['cw'] as $cid => $width) {
05786                 $cid -= $cidoffset;
05787                 if ($width != $font['dw']) {
05788                     if ($cid == ($prevcid + 1)) {
05789                         // consecutive CID
05790                         if ($width == $prevwidth) {
05791                             if ($width == $range[$rangeid][0]) {
05792                                 $range[$rangeid][] = $width;
05793                             } else {
05794                                 array_pop($range[$rangeid]);
05795                                 // new range
05796                                 $rangeid = $prevcid;
05797                                 $range[$rangeid] = array();
05798                                 $range[$rangeid][] = $prevwidth;
05799                                 $range[$rangeid][] = $width;
05800                             }
05801                             $interval = true;
05802                             $range[$rangeid]['interval'] = true;
05803                         } else {
05804                             if ($interval) {
05805                                 // new range
05806                                 $rangeid = $cid;
05807                                 $range[$rangeid] = array();
05808                                 $range[$rangeid][] = $width;
05809                             } else {
05810                                 $range[$rangeid][] = $width;
05811                             }
05812                             $interval = false;
05813                         }
05814                     } else {
05815                         // new range
05816                         $rangeid = $cid;
05817                         $range[$rangeid] = array();
05818                         $range[$rangeid][] = $width;
05819                         $interval = false;
05820                     }
05821                     $prevcid = $cid;
05822                     $prevwidth = $width;
05823                 }
05824             }
05825             // optimize ranges
05826             $prevk = -1;
05827             $nextk = -1;
05828             $prevint = false;
05829             foreach ($range as $k => $ws) {
05830                 $cws = count($ws);
05831                 if (($k == $nextk) AND (!$prevint) AND ((!isset($ws['interval'])) OR ($cws < 4))) {
05832                     if (isset($range[$k]['interval'])) {
05833                         unset($range[$k]['interval']);
05834                     }
05835                     $range[$prevk] = array_merge($range[$prevk], $range[$k]);
05836                     unset($range[$k]);
05837                 } else {
05838                     $prevk = $k;
05839                 }
05840                 $nextk = $k + $cws;
05841                 if (isset($ws['interval'])) {
05842                     if ($cws > 3) {
05843                         $prevint = true;
05844                     } else {
05845                         $prevint = false;
05846                     }
05847                     unset($range[$k]['interval']);
05848                     --$nextk;
05849                 } else {
05850                     $prevint = false;
05851                 }
05852             }
05853             // output data
05854             $w = '';
05855             foreach ($range as $k => $ws) {
05856                 if (count(array_count_values($ws)) == 1) {
05857                     // interval mode is more compact
05858                     $w .= ' '.$k.' '.($k + count($ws) - 1).' '.$ws[0];
05859                 } else {
05860                     // range mode
05861                     $w .= ' '.$k.' [ '.implode(' ', $ws).' ]';
05862                 }
05863             }
05864             $this->_out('/W ['.$w.' ]');
05865         }
05866         
05875         protected function _puttruetypeunicode($font) {
05876             // Type0 Font
05877             // A composite font composed of other fonts, organized hierarchically
05878             $this->_newobj();
05879             $this->_out('<</Type /Font');
05880             $this->_out('/Subtype /Type0');
05881             $this->_out('/BaseFont /'.$font['name'].'');
05882             $this->_out('/Encoding /Identity-H'); //The horizontal identity mapping for 2-byte CIDs; may be used with CIDFonts using any Registry, Ordering, and Supplement values.
05883             $this->_out('/ToUnicode /Identity-H');
05884             $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]');
05885             $this->_out('>>');
05886             $this->_out('endobj');
05887             // CIDFontType2
05888             // A CIDFont whose glyph descriptions are based on TrueType font technology
05889             $this->_newobj();
05890             $this->_out('<</Type /Font');
05891             $this->_out('/Subtype /CIDFontType2');
05892             $this->_out('/BaseFont /'.$font['name'].'');
05893             // A dictionary containing entries that define the character collection of the CIDFont.
05894             $cidinfo = '/Registry '.$this->_datastring('Adobe');
05895             $cidinfo .= ' /Ordering '.$this->_datastring('Identity');
05896             $cidinfo .= ' /Supplement 0';
05897             $this->_out('/CIDSystemInfo <<'.$cidinfo.'>>');
05898             $this->_out('/FontDescriptor '.($this->n + 1).' 0 R');
05899             $this->_out('/DW '.$font['dw'].''); // default width
05900             $this->_putfontwidths($font, 0);
05901             $this->_out('/CIDToGIDMap '.($this->n + 2).' 0 R');
05902             $this->_out('>>');
05903             $this->_out('endobj');          
05904             // Font descriptor
05905             // A font descriptor describing the CIDFont default metrics other than its glyph widths
05906             $this->_newobj();
05907             $this->_out('<</Type /FontDescriptor');
05908             $this->_out('/FontName /'.$font['name']);
05909             foreach ($font['desc'] as $key => $value) {
05910                 $this->_out('/'.$key.' '.$value);
05911             }
05912             $fontdir = '';
05913             if (!$this->empty_string($font['file'])) {
05914                 // A stream containing a TrueType font
05915                 $this->_out('/FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R');
05916                 $fontdir = $this->FontFiles[$font['file']]['fontdir'];
05917             }
05918             $this->_out('>>');
05919             $this->_out('endobj');
05920             $this->_newobj();
05921             if (isset($font['ctg']) AND (!$this->empty_string($font['ctg']))) {
05922                 // Embed CIDToGIDMap
05923                 // A specification of the mapping from CIDs to glyph indices
05924                 // search and get CTG font file to embedd
05925                 $ctgfile = strtolower($font['ctg']);
05926                 // search and get ctg font file to embedd
05927                 $fontfile = '';
05928                 // search files on various directories
05929                 if (file_exists($fontdir.$ctgfile)) {
05930                     $fontfile = $fontdir.$ctgfile;
05931                 } elseif (file_exists($this->_getfontpath().$ctgfile)) {
05932                     $fontfile = $this->_getfontpath().$ctgfile;
05933                 } elseif (file_exists($ctgfile)) {
05934                     $fontfile = $ctgfile;
05935                 }
05936                 if ($this->empty_string($fontfile)) {
05937                     $this->Error('Font file not found: '.$ctgfile);
05938                 }
05939                 $size = filesize($fontfile);
05940                 $this->_out('<</Length '.$size.'');
05941                 if (substr($fontfile, -2) == '.z') { // check file extension
05942                     // Decompresses data encoded using the public-domain 
05943                     // zlib/deflate compression method, reproducing the 
05944                     // original text or binary data
05945                     $this->_out('/Filter /FlateDecode');
05946                 }
05947                 $this->_out('>>');
05948                 $this->_putstream(file_get_contents($fontfile));
05949             }
05950             $this->_out('endobj');
05951         }
05952         
05960         protected function _putcidfont0($font) {
05961             $cidoffset = 31;
05962             if (isset($font['cidinfo']['uni2cid'])) {
05963                 // convert unicode to cid.
05964                 $uni2cid = $font['cidinfo']['uni2cid'];
05965                 $cw = array();
05966                 foreach ($font['cw'] as $uni => $width) {
05967                     if (isset($uni2cid[$uni])) {
05968                         $cw[($uni2cid[$uni] + $cidoffset)] = $width;
05969                     } elseif ($uni < 256) {
05970                         $cw[$uni] = $width;
05971                     } // else unknown character
05972                 }
05973                 $font = array_merge($font, array('cw' => $cw));
05974             }
05975             $name = $font['name'];
05976             $enc = $font['enc'];
05977             if ($enc) {
05978                 $longname = $name.'-'.$enc;
05979             } else {
05980                 $longname = $name;
05981             }
05982             $this->_newobj();
05983             $this->_out('<</Type /Font');
05984             $this->_out('/BaseFont /'.$longname);
05985             $this->_out('/Subtype /Type0');
05986             if ($enc) {
05987                 $this->_out('/Encoding /'.$enc);
05988             }
05989             $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]');
05990             $this->_out('>>');
05991             $this->_out('endobj');
05992             $this->_newobj();
05993             $this->_out('<</Type /Font');
05994             $this->_out('/BaseFont /'.$name);
05995             $this->_out('/Subtype /CIDFontType0');
05996             $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry']);
05997             $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering']);
05998             $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
05999             $this->_out('/CIDSystemInfo <<'.$cidinfo.'>>');
06000             $this->_out('/FontDescriptor '.($this->n + 1).' 0 R');
06001             $this->_out('/DW '.$font['dw']);
06002             $this->_putfontwidths($font, $cidoffset);
06003             $this->_out('>>');
06004             $this->_out('endobj');
06005             $this->_newobj();
06006             $s = '<</Type /FontDescriptor /FontName /'.$name;
06007             foreach ($font['desc'] as $k => $v) {
06008                 if ($k != 'Style') {
06009                     $s .= ' /'.$k.' '.$v.'';
06010                 }
06011             }
06012             $this->_out($s.'>>');
06013             $this->_out('endobj');
06014         }
06015 
06020         protected function _putimages() {
06021             $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
06022             foreach ($this->imagekeys as $file) {
06023                 $info = $this->getImageBuffer($file);
06024                 $this->_newobj();
06025                 $this->setImageSubBuffer($file, 'n', $this->n);
06026                 $this->_out('<</Type /XObject');
06027                 $this->_out('/Subtype /Image');
06028                 $this->_out('/Width '.$info['w']);
06029                 $this->_out('/Height '.$info['h']);
06030                 if (isset($info['masked'])) {
06031                     $this->_out('/SMask '.($this->n - 1).' 0 R');
06032                 }
06033                 if ($info['cs'] == 'Indexed') {
06034                     $this->_out('/ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n + 1).' 0 R]');
06035                 } else {
06036                     $this->_out('/ColorSpace /'.$info['cs']);
06037                     if ($info['cs'] == 'DeviceCMYK') {
06038                         $this->_out('/Decode [1 0 1 0 1 0 1 0]');
06039                     }
06040                 }
06041                 $this->_out('/BitsPerComponent '.$info['bpc']);
06042                 if (isset($info['f'])) {
06043                     $this->_out('/Filter /'.$info['f']);
06044                 }
06045                 if (isset($info['parms'])) {
06046                     $this->_out($info['parms']);
06047                 }
06048                 if (isset($info['trns']) AND is_array($info['trns'])) {
06049                     $trns='';
06050                     $count_info = count($info['trns']);
06051                     for($i=0; $i < $count_info; ++$i) {
06052                         $trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
06053                     }
06054                     $this->_out('/Mask ['.$trns.']');
06055                 }
06056                 $this->_out('/Length '.strlen($info['data']).'>>');
06057                 $this->_putstream($info['data']);
06058                 $this->_out('endobj');
06059                 //Palette
06060                 if ($info['cs'] == 'Indexed') {
06061                     $this->_newobj();
06062                     $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
06063                     $this->_out('<<'.$filter.'/Length '.strlen($pal).'>>');
06064                     $this->_putstream($pal);
06065                     $this->_out('endobj');
06066                 }
06067             }
06068         }
06069 
06075         protected function _putspotcolors() {
06076             foreach ($this->spot_colors as $name => $color) {
06077                 $this->_newobj();
06078                 $this->spot_colors[$name]['n'] = $this->n;
06079                 $this->_out('[/Separation /'.str_replace(' ', '#20', $name));
06080                 $this->_out('/DeviceCMYK <<');
06081                 $this->_out('/Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0] ');
06082                 $this->_out(sprintf('/C1 [%.4F %.4F %.4F %.4F] ', $color['c']/100, $color['m']/100, $color['y']/100, $color['k']/100));
06083                 $this->_out('/FunctionType 2 /Domain [0 1] /N 1>>]');
06084                 $this->_out('endobj');
06085             }
06086         }
06087 
06092         protected function _putxobjectdict() {
06093             foreach ($this->imagekeys as $file) {
06094                 $info = $this->getImageBuffer($file);
06095                 $this->_out('/I'.$info['i'].' '.$info['n'].' 0 R');
06096             }
06097         }
06098 
06103         protected function _putresourcedict() {
06104             $this->_out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
06105             $this->_out('/Font <<');
06106             foreach ($this->fontkeys as $fontkey) {
06107                 $font = $this->getFontBuffer($fontkey);
06108                 $this->_out('/F'.$font['i'].' '.$font['n'].' 0 R');
06109             }
06110             $this->_out('>>');
06111             $this->_out('/XObject <<');
06112             $this->_putxobjectdict();
06113             $this->_out('>>');
06114             // visibility
06115             $this->_out('/Properties <</OC1 '.$this->n_ocg_print.' 0 R /OC2 '.$this->n_ocg_view.' 0 R>>');
06116             // transparency
06117             $this->_out('/ExtGState <<');
06118             foreach ($this->extgstates as $k => $extgstate) {
06119                 $this->_out('/GS'.$k.' '.$extgstate['n'].' 0 R');
06120             }
06121             $this->_out('>>');
06122             // gradients
06123             if (isset($this->gradients) AND (count($this->gradients) > 0)) {
06124                 $this->_out('/Shading <<');
06125                 foreach ($this->gradients as $id => $grad) {
06126                     $this->_out('/Sh'.$id.' '.$grad['id'].' 0 R');
06127                 }
06128                 $this->_out('>>');
06129             }
06130             // spot colors
06131             if (isset($this->spot_colors) AND (count($this->spot_colors) > 0)) {
06132                 $this->_out('/ColorSpace <<');
06133                 foreach ($this->spot_colors as $color) {
06134                     $this->_out('/CS'.$color['i'].' '.$color['n'].' 0 R');
06135                 }
06136                 $this->_out('>>');
06137             }
06138         }
06139         
06144         protected function _putresources() {
06145             $this->_putextgstates();
06146             $this->_putocg();
06147             $this->_putfonts();
06148             $this->_putimages();
06149             $this->_putspotcolors();
06150             $this->_putshaders();
06151             //Resource dictionary
06152             $this->offsets[2] = $this->bufferlen;
06153             $this->_out('2 0 obj');
06154             $this->_out('<<');
06155             $this->_putresourcedict();
06156             $this->_out('>>');
06157             $this->_out('endobj');
06158             $this->_putjavascript();
06159             $this->_putbookmarks();
06160             $this->_putEmbeddedFiles();
06161             // encryption
06162             if ($this->encrypted) {
06163                 $this->_newobj();
06164                 $this->enc_obj_id = $this->n;
06165                 $this->_out('<<');
06166                 $this->_putencryption();
06167                 $this->_out('>>');
06168                 $this->_out('endobj');
06169             }
06170         }
06171         
06177         protected function _putinfo() {
06178             if (!$this->empty_string($this->title)) {
06179                 $this->_out('/Title '.$this->_textstring($this->title));
06180             }
06181             if (!$this->empty_string($this->author)) {
06182                 $this->_out('/Author '.$this->_textstring($this->author));
06183             }
06184             if (!$this->empty_string($this->subject)) {
06185                 $this->_out('/Subject '.$this->_textstring($this->subject));
06186             }
06187             if (!$this->empty_string($this->keywords)) {
06188                 $this->_out('/Keywords '.$this->_textstring($this->keywords));
06189             }
06190             if (!$this->empty_string($this->creator)) {
06191                 $this->_out('/Creator '.$this->_textstring($this->creator));
06192             }
06193             if (defined('PDF_PRODUCER')) {
06194                 $this->_out('/Producer '.$this->_textstring(PDF_PRODUCER));
06195             }
06196             $this->_out('/CreationDate '.$this->_datastring('D:'.date('YmdHis')));
06197             $this->_out('/ModDate '.$this->_datastring('D:'.date('YmdHis')));   
06198         }
06199         
06204         protected function _putcatalog() {
06205             $this->_out('/Type /Catalog');
06206             $this->_out('/Pages 1 0 R');
06207             if ($this->ZoomMode == 'fullpage') {
06208                 $this->_out('/OpenAction [3 0 R /Fit]');
06209             } elseif ($this->ZoomMode == 'fullwidth') {
06210                 $this->_out('/OpenAction [3 0 R /FitH null]');
06211             } elseif ($this->ZoomMode == 'real') {
06212                 $this->_out('/OpenAction [3 0 R /XYZ null null 1]');
06213             } elseif (!is_string($this->ZoomMode)) {
06214                 $this->_out('/OpenAction [3 0 R /XYZ null null '.($this->ZoomMode / 100).']');
06215             }
06216             if (isset($this->LayoutMode) AND (!$this->empty_string($this->LayoutMode))) {
06217                 $this->_out('/PageLayout /'.$this->LayoutMode.'');
06218             }
06219             if (isset($this->PageMode) AND (!$this->empty_string($this->PageMode))) {
06220                 $this->_out('/PageMode /'.$this->PageMode);
06221             }
06222             if (isset($this->l['a_meta_language'])) {
06223                 $this->_out('/Lang /'.$this->l['a_meta_language']);
06224             }
06225             $this->_out('/Names <<');
06226             if (!$this->empty_string($this->javascript)) {
06227                 $this->_out('/JavaScript '.($this->n_js).' 0 R');
06228             }
06229             $this->_out('>>');
06230             if (count($this->outlines) > 0) {
06231                 $this->_out('/Outlines '.$this->OutlineRoot.' 0 R');
06232                 $this->_out('/PageMode /UseOutlines');
06233             }
06234             $this->_putviewerpreferences();
06235             $p = $this->n_ocg_print.' 0 R';
06236             $v = $this->n_ocg_view.' 0 R';
06237             $as = '<</Event /Print /OCGs ['.$p.' '.$v.'] /Category [/Print]>> <</Event /View /OCGs ['.$p.' '.$v.'] /Category [/View]>>';
06238             $this->_out('/OCProperties <</OCGs ['.$p.' '.$v.'] /D <</ON ['.$p.'] /OFF ['.$v.'] /AS ['.$as.']>>>>');
06239         }
06240         
06247         protected function _putviewerpreferences() {
06248             $this->_out('/ViewerPreferences<<');
06249             if ($this->rtl) {
06250                 $this->_out('/Direction /R2L');
06251             } else {
06252                 $this->_out('/Direction /L2R');
06253             }
06254             if (isset($this->viewer_preferences['HideToolbar']) AND ($this->viewer_preferences['HideToolbar'])) {
06255                 $this->_out('/HideToolbar true');
06256             }
06257             if (isset($this->viewer_preferences['HideMenubar']) AND ($this->viewer_preferences['HideMenubar'])) {
06258                 $this->_out('/HideMenubar true');
06259             }
06260             if (isset($this->viewer_preferences['HideWindowUI']) AND ($this->viewer_preferences['HideWindowUI'])) {
06261                 $this->_out('/HideWindowUI true');
06262             }
06263             if (isset($this->viewer_preferences['FitWindow']) AND ($this->viewer_preferences['FitWindow'])) {
06264                 $this->_out('/FitWindow true');
06265             }
06266             if (isset($this->viewer_preferences['CenterWindow']) AND ($this->viewer_preferences['CenterWindow'])) {
06267                 $this->_out('/CenterWindow true');
06268             }
06269             if (isset($this->viewer_preferences['DisplayDocTitle']) AND ($this->viewer_preferences['DisplayDocTitle'])) {
06270                 $this->_out('/DisplayDocTitle true');
06271             }
06272             if (isset($this->viewer_preferences['NonFullScreenPageMode'])) {
06273                 $this->_out('/NonFullScreenPageMode /'.$this->viewer_preferences['NonFullScreenPageMode'].'');
06274             }
06275             if (isset($this->viewer_preferences['ViewArea'])) {
06276                 $this->_out('/ViewArea /'.$this->viewer_preferences['ViewArea']);
06277             }
06278             if (isset($this->viewer_preferences['ViewClip'])) {
06279                 $this->_out('/ViewClip /'.$this->viewer_preferences['ViewClip']);
06280             }
06281             if (isset($this->viewer_preferences['PrintArea'])) {
06282                 $this->_out('/PrintArea /'.$this->viewer_preferences['PrintArea']);
06283             }
06284             if (isset($this->viewer_preferences['PrintClip'])) {
06285                 $this->_out('/PrintClip /'.$this->viewer_preferences['PrintClip']);
06286             }
06287             if (isset($this->viewer_preferences['PrintScaling'])) {
06288                 $this->_out('/PrintScaling /'.$this->viewer_preferences['PrintScaling']);
06289             }
06290             if (isset($this->viewer_preferences['Duplex']) AND (!$this->empty_string($this->viewer_preferences['Duplex']))) {
06291                 $this->_out('/Duplex /'.$this->viewer_preferences['Duplex']);
06292             }
06293             if (isset($this->viewer_preferences['PickTrayByPDFSize'])) {
06294                 if ($this->viewer_preferences['PickTrayByPDFSize']) {
06295                     $this->_out('/PickTrayByPDFSize true');
06296                 } else {
06297                     $this->_out('/PickTrayByPDFSize false');
06298                 }
06299             }
06300             if (isset($this->viewer_preferences['PrintPageRange'])) {
06301                 $PrintPageRangeNum = '';
06302                 foreach ($this->viewer_preferences['PrintPageRange'] as $k => $v) {
06303                     $PrintPageRangeNum .= ' '.($v - 1).'';
06304                 }
06305                 $this->_out('/PrintPageRange ['.substr($PrintPageRangeNum,1).']');
06306             }
06307             if (isset($this->viewer_preferences['NumCopies'])) {
06308                 $this->_out('/NumCopies '.intval($this->viewer_preferences['NumCopies']));
06309             }
06310             $this->_out('>>');
06311         }
06312 
06317         protected function _puttrailer() {
06318             $this->_out('/Size '.($this->n + 1));
06319             $this->_out('/Root '.$this->n.' 0 R');
06320             $this->_out('/Info '.($this->n - 1).' 0 R');
06321             if ($this->encrypted) {
06322                 $this->_out('/Encrypt '.$this->enc_obj_id.' 0 R');
06323                 $this->_out('/ID [()()]');
06324             }
06325         }
06326 
06331         protected function _putheader() {
06332             $this->_out('%PDF-'.$this->PDFVersion);
06333         }
06334 
06339         protected function _enddoc() {
06340             $this->state = 1;
06341             $this->_putheader();
06342             $this->_putpages();
06343             $this->_putresources();
06344             //Info
06345             $this->_newobj();
06346             $this->_out('<<');
06347             $this->_putinfo();
06348             $this->_out('>>');
06349             $this->_out('endobj');
06350             //Catalog
06351             $this->_newobj();
06352             $this->_out('<<');
06353             $this->_putcatalog();
06354             $this->_putcertification();
06355             $this->_putuserrights();
06356             $this->_out('>>');
06357             $this->_out('endobj');
06358             //Cross-ref
06359             $o = $this->bufferlen;
06360             $this->_out('xref');
06361             $this->_out('0 '.($this->n + 1));
06362             $this->_out('0000000000 65535 f ');
06363             for($i=1; $i <= $this->n; ++$i) {
06364                 $this->_out(sprintf('%010d 00000 n ',$this->offsets[$i]));
06365             }
06366             foreach ($this->embeddedfiles as $filename => $filedata) {
06367                 $this->_out(sprintf('%010d 00000 n ',$this->offsets[$filedata['n']]));
06368             }
06369             //Trailer
06370             $this->_out('trailer');
06371             $this->_out('<<');
06372             $this->_puttrailer();
06373             $this->_out('>>');
06374             $this->_out('startxref');
06375             $this->_out($o);
06376             $this->_out('%%EOF');
06377             $this->state = 3; // end-of-doc
06378             if ($this->diskcache) {
06379                 // remove temporary files used for images
06380                 foreach ($this->imagekeys as $key) {
06381                     // remove temporary files
06382                     unlink($this->images[$key]);
06383                 }
06384                 foreach ($this->fontkeys as $key) {
06385                     // remove temporary files
06386                     unlink($this->fonts[$key]);
06387                 }
06388             }
06389         }
06390 
06397         protected function _beginpage($orientation='', $format='') {
06398             ++$this->page;
06399             $this->setPageBuffer($this->page, '');
06400             // initialize array for graphics tranformation positions inside a page buffer
06401             $this->transfmrk[$this->page] = array();
06402             $this->state = 2;
06403             if ($this->empty_string($orientation)) {
06404                 if (isset($this->CurOrientation)) {
06405                     $orientation = $this->CurOrientation;
06406                 } else {
06407                     $orientation = 'P';
06408                 }
06409             }
06410             if ($this->empty_string($format)) {
06411                 $this->setPageOrientation($orientation);
06412             } else {
06413                 $this->setPageFormat($format, $orientation);
06414             }
06415             if ($this->rtl) {
06416                 $this->x = $this->w - $this->rMargin;
06417             } else {
06418                 $this->x = $this->lMargin;
06419             }
06420             $this->y = $this->tMargin;
06421             if (isset($this->newpagegroup[$this->page])) {
06422                 // start a new group
06423                 $n = sizeof($this->pagegroups) + 1;
06424                 $alias = '{nb'.$n.'}';
06425                 $this->pagegroups[$alias] = 1;
06426                 $this->currpagegroup = $alias;
06427             } elseif ($this->currpagegroup) {
06428                 ++$this->pagegroups[$this->currpagegroup];
06429             }
06430         }
06431 
06436         protected function _endpage() {
06437             $this->setVisibility('all');
06438             $this->state = 1;
06439         }
06440 
06445         protected function _newobj() {
06446             ++$this->n;
06447             $this->offsets[$this->n] = $this->bufferlen;
06448             $this->_out($this->n.' 0 obj');
06449         }
06450 
06458         protected function _dounderline($x, $y, $txt) {
06459             $up = $this->CurrentFont['up'];
06460             $ut = $this->CurrentFont['ut'];
06461             $w = $this->GetStringWidth($txt);
06462             return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, ($this->h - ($y - $up / 1000 * $this->FontSize)) * $this->k, $w * $this->k, -$ut / 1000 * $this->FontSizePt);
06463         }
06464         
06472         protected function _dolinethrough($x, $y, $txt) {
06473             $up = $this->CurrentFont['up'];
06474             $ut = $this->CurrentFont['ut'];
06475             $w = $this->GetStringWidth($txt);
06476             return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, ($this->h - ($y - ($this->FontSize/2) - $up / 1000 * $this->FontSize)) * $this->k, $w * $this->k, -$ut / 1000 * $this->FontSizePt);
06477         }
06478         
06485         protected function _freadint($f) {
06486             $a = unpack('Ni', fread($f, 4));
06487             return $a['i'];
06488         }
06489         
06496         protected function _escape($s) {
06497             // the chr(13) substitution fixes the Bugs item #1421290.
06498             return strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r'));
06499         }
06500         
06507         protected function _datastring($s) {
06508             if ($this->encrypted) {
06509                 $s = $this->_RC4($this->_objectkey($this->n), $s);
06510             }
06511             return '('. $this->_escape($s).')';
06512         }
06513         
06520         protected function _textstring($s) {
06521             if ($this->isunicode) {
06522                 //Convert string to UTF-16BE
06523                 $s = $this->UTF8ToUTF16BE($s, true);
06524             }
06525             return $this->_datastring($s);
06526         }
06527                 
06534         protected function _escapetext($s) {
06535             if ($this->isunicode) {
06536                 if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
06537                     $s = $this->UTF8ToLatin1($s);
06538                 } else {
06539                     //Convert string to UTF-16BE and reverse RTL language
06540                     $s = $this->utf8StrRev($s, false, $this->tmprtl);
06541                 }
06542             }
06543             return $this->_escape($s);
06544         }
06545         
06551         protected function _putstream($s) {
06552             if ($this->encrypted) {
06553                 $s = $this->_RC4($this->_objectkey($this->n), $s);
06554             }
06555             $this->_out('stream');
06556             $this->_out($s);
06557             $this->_out('endstream');
06558         }
06559         
06565         protected function _out($s) {
06566             if ($this->state == 2) {
06567                 if ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
06568                     // puts data before page footer
06569                     $pagebuff = $this->getPageBuffer($this->page);
06570                     $page = substr($pagebuff, 0, -$this->footerlen[$this->page]);
06571                     $footer = substr($pagebuff, -$this->footerlen[$this->page]);
06572                     $this->setPageBuffer($this->page, $page.$s."\n".$footer);
06573                     // update footer position
06574                     $this->footerpos[$this->page] += strlen($s."\n");   
06575                 } else {
06576                     $this->setPageBuffer($this->page, $s."\n", true);
06577                 }
06578             } else {
06579                 $this->setBuffer($s."\n");
06580             }
06581         }
06582         
06617         protected function UTF8StringToArray($str) {
06618             if (isset($this->cache_UTF8StringToArray['_'.$str])) {
06619                 // return cached value
06620                 return($this->cache_UTF8StringToArray['_'.$str]);
06621             }
06622             // check cache size
06623             if ($this->cache_size_UTF8StringToArray >= $this->cache_maxsize_UTF8StringToArray) {
06624                 // remove first element
06625                 array_shift($this->cache_UTF8StringToArray);
06626             }
06627             ++$this->cache_size_UTF8StringToArray;
06628             if (!$this->isunicode) {
06629                 // split string into array of equivalent codes
06630                 $strarr = array();
06631                 $strlen = strlen($str);
06632                 for($i=0; $i < $strlen; ++$i) {
06633                     $strarr[] = ord($str{$i});
06634                 }
06635                 // insert new value on cache
06636                 $this->cache_UTF8StringToArray['_'.$str] = $strarr;
06637                 return $strarr;
06638             }
06639             $unicode = array(); // array containing unicode values
06640             $bytes  = array(); // array containing single character byte sequences
06641             $numbytes  = 1; // number of octetc needed to represent the UTF-8 character
06642             $str .= ''; // force $str to be a string
06643             $length = strlen($str);
06644             for($i = 0; $i < $length; ++$i) {
06645                 $char = ord($str{$i}); // get one string character at time
06646                 if (count($bytes) == 0) { // get starting octect
06647                     if ($char <= 0x7F) {
06648                         $unicode[] = $char; // use the character "as is" because is ASCII
06649                         $numbytes = 1;
06650                     } elseif (($char >> 0x05) == 0x06) { // 2 bytes character (0x06 = 110 BIN)
06651                         $bytes[] = ($char - 0xC0) << 0x06; 
06652                         $numbytes = 2;
06653                     } elseif (($char >> 0x04) == 0x0E) { // 3 bytes character (0x0E = 1110 BIN)
06654                         $bytes[] = ($char - 0xE0) << 0x0C; 
06655                         $numbytes = 3;
06656                     } elseif (($char >> 0x03) == 0x1E) { // 4 bytes character (0x1E = 11110 BIN)
06657                         $bytes[] = ($char - 0xF0) << 0x12; 
06658                         $numbytes = 4;
06659                     } else {
06660                         // use replacement character for other invalid sequences
06661                         $unicode[] = 0xFFFD;
06662                         $bytes = array();
06663                         $numbytes = 1;
06664                     }
06665                 } elseif (($char >> 0x06) == 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN
06666                     $bytes[] = $char - 0x80;
06667                     if (count($bytes) == $numbytes) {
06668                         // compose UTF-8 bytes to a single unicode value
06669                         $char = $bytes[0];
06670                         for($j = 1; $j < $numbytes; ++$j) {
06671                             $char += ($bytes[$j] << (($numbytes - $j - 1) * 0x06));
06672                         }
06673                         if ((($char >= 0xD800) AND ($char <= 0xDFFF)) OR ($char >= 0x10FFFF)) {
06674                             /* The definition of UTF-8 prohibits encoding character numbers between
06675                             U+D800 and U+DFFF, which are reserved for use with the UTF-16
06676                             encoding form (as surrogate pairs) and do not directly represent
06677                             characters. */
06678                             $unicode[] = 0xFFFD; // use replacement character
06679                         } else {
06680                             $unicode[] = $char; // add char to array
06681                         }
06682                         // reset data for next char
06683                         $bytes = array(); 
06684                         $numbytes = 1;
06685                     }
06686                 } else {
06687                     // use replacement character for other invalid sequences
06688                     $unicode[] = 0xFFFD;
06689                     $bytes = array();
06690                     $numbytes = 1;
06691                 }
06692             }
06693             // insert new value on cache
06694             $this->cache_UTF8StringToArray['_'.$str] = $unicode;
06695             return $unicode;
06696         }
06697         
06708         protected function UTF8ToUTF16BE($str, $setbom=true) {
06709             if (!$this->isunicode) {
06710                 return $str; // string is not in unicode
06711             }
06712             $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
06713             return $this->arrUTF8ToUTF16BE($unicode, $setbom);
06714         }
06715         
06724         protected function UTF8ToLatin1($str) {
06725             global $utf8tolatin;
06726             if (!$this->isunicode) {
06727                 return $str; // string is not in unicode
06728             }
06729             $outstr = ''; // string to be returned
06730             $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
06731             foreach ($unicode as $char) {
06732                 if ($char < 256) {
06733                     $outstr .= chr($char);
06734                 } elseif (array_key_exists($char, $utf8tolatin)) {
06735                     // map from UTF-8
06736                     $outstr .= chr($utf8tolatin[$char]);
06737                 } elseif ($char == 0xFFFD) {
06738                     // skip
06739                 } else {
06740                     $outstr .= '?';
06741                 }
06742             }
06743             return $outstr;
06744         }
06745 
06784         protected function arrUTF8ToUTF16BE($unicode, $setbom=true) {
06785             $outstr = ''; // string to be returned
06786             if ($setbom) {
06787                 $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM)
06788             }
06789             foreach ($unicode as $char) {
06790                 if ($char == 0xFFFD) {
06791                     $outstr .= "\xFF\xFD"; // replacement character
06792                 } elseif ($char < 0x10000) {
06793                     $outstr .= chr($char >> 0x08);
06794                     $outstr .= chr($char & 0xFF);
06795                 } else {
06796                     $char -= 0x10000;
06797                     $w1 = 0xD800 | ($char >> 0x10);
06798                     $w2 = 0xDC00 | ($char & 0x3FF); 
06799                     $outstr .= chr($w1 >> 0x08);
06800                     $outstr .= chr($w1 & 0xFF);
06801                     $outstr .= chr($w2 >> 0x08);
06802                     $outstr .= chr($w2 & 0xFF);
06803                 }
06804             }
06805             return $outstr;
06806         }
06807         // ====================================================
06808         
06815         public function setHeaderFont($font) {
06816             $this->header_font = $font;
06817         }
06818         
06825         public function getHeaderFont() {
06826             return $this->header_font;
06827         }
06828         
06835         public function setFooterFont($font) {
06836             $this->footer_font = $font;
06837         }
06838         
06845         public function getFooterFont() {
06846             return $this->footer_font;
06847         }
06848         
06855         public function setLanguageArray($language) {
06856             $this->l = $language;
06857             $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
06858         }
06859         
06864         public function getPDFData() {
06865             if ($this->state < 3) {
06866                 $this->Close();
06867             }
06868             return $this->buffer;
06869         }
06870                 
06882         public function addHtmlLink($url, $name, $fill=0, $firstline=false, $color='', $style=-1) {
06883             if (!$this->empty_string($url) AND ($url{0} == '#')) {
06884                 // convert url to internal link
06885                 $page = intval(substr($url, 1));
06886                 $url = $this->AddLink();
06887                 $this->SetLink($url, 0, $page);
06888             }
06889             // store current settings
06890             $prevcolor = $this->fgcolor;
06891             $prevstyle = $this->FontStyle;
06892             if (empty($color)) {
06893                 $this->SetTextColorArray($this->htmlLinkColorArray);
06894             } else {
06895                 $this->SetTextColorArray($color);
06896             }
06897             if ($style == -1) {
06898                 $this->SetFont('', $this->FontStyle.$this->htmlLinkFontStyle);
06899             } else {
06900                 $this->SetFont('', $this->FontStyle.$style);
06901             }
06902             $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline);
06903             // restore settings
06904             $this->SetFont('', $prevstyle);
06905             $this->SetTextColorArray($prevcolor);
06906             return $ret;
06907         }
06908         
06915         public function convertHTMLColorToDec($color='#FFFFFF') {
06916             global $webcolor;
06917             $color = preg_replace('/[\s]*/', '', $color); // remove extra spaces
06918             $color = strtolower($color);
06919             if (strlen($color) == 0) {
06920                 return false;
06921             }
06922             if (substr($color, 0, 3) == 'rgb') {
06923                 $codes = substr($color, 4);
06924                 $codes = str_replace(')', '', $codes);
06925                 $returncolor = explode(',', $codes, 3);
06926                 return $returncolor;
06927             }
06928             if (substr($color, 0, 1) != '#') {
06929                 // decode color name
06930                 if (isset($webcolor[$color])) {
06931                     $color_code = $webcolor[$color];
06932                 } else {
06933                     return false;
06934                 }
06935             } else {
06936                 $color_code = substr($color, 1);
06937             }
06938             switch (strlen($color_code)) {
06939                 case 3: {
06940                     // three-digit hexadecimal representation
06941                     $r = substr($color_code, 0, 1);
06942                     $g = substr($color_code, 1, 1);
06943                     $b = substr($color_code, 2, 1);
06944                     $returncolor['R'] = hexdec($r.$r);
06945                     $returncolor['G'] = hexdec($g.$g);
06946                     $returncolor['B'] = hexdec($b.$b);
06947                     break;
06948                 }
06949                 case 6: {
06950                     // six-digit hexadecimal representation
06951                     $returncolor['R'] = hexdec(substr($color_code, 0, 2));
06952                     $returncolor['G'] = hexdec(substr($color_code, 2, 2));
06953                     $returncolor['B'] = hexdec(substr($color_code, 4, 2));
06954                     break;
06955                 }
06956             }
06957             return $returncolor;
06958         }
06959         
06967         public function pixelsToUnits($px) {
06968             return ($px / ($this->imgscale * $this->k));
06969         }
06970             
06978         public function unhtmlentities($text_to_convert) {
06979             return html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding);
06980         }
06981         
06982         // ENCRYPTION METHODS ----------------------------------
06983         // SINCE 2.0.000 (2008-01-02)
06984         
06991         protected function _objectkey($n) {
06992             return substr($this->_md5_16($this->encryption_key.pack('VXxx', $n)), 0, 10);
06993         }
06994         
07000         protected function _putencryption() {
07001             $this->_out('/Filter /Standard');
07002             $this->_out('/V 1');
07003             $this->_out('/R 2');
07004             $this->_out('/O ('.$this->_escape($this->Ovalue).')');
07005             $this->_out('/U ('.$this->_escape($this->Uvalue).')');
07006             $this->_out('/P '.$this->Pvalue);
07007         }
07008         
07019         protected function _RC4($key, $text) {
07020             if ($this->last_rc4_key != $key) {
07021                 $k = str_repeat($key, ((256 / strlen($key)) + 1));
07022                 $rc4 = range(0, 255);
07023                 $j = 0;
07024                 for ($i = 0; $i < 256; ++$i) {
07025                     $t = $rc4[$i];
07026                     $j = ($j + $t + ord($k{$i})) % 256;
07027                     $rc4[$i] = $rc4[$j];
07028                     $rc4[$j] = $t;
07029                 }
07030                 $this->last_rc4_key = $key;
07031                 $this->last_rc4_key_c = $rc4;
07032             } else {
07033                 $rc4 = $this->last_rc4_key_c;
07034             }
07035             $len = strlen($text);
07036             $a = 0;
07037             $b = 0;
07038             $out = '';
07039             for ($i = 0; $i < $len; ++$i) {
07040                 $a = ($a + 1) % 256;
07041                 $t = $rc4[$a];
07042                 $b = ($b + $t) % 256;
07043                 $rc4[$a] = $rc4[$b];
07044                 $rc4[$b] = $t;
07045                 $k = $rc4[($rc4[$a] + $rc4[$b]) % 256];
07046                 $out .= chr(ord($text{$i}) ^ $k);
07047             }
07048             return $out;
07049         }
07050         
07059         protected function _md5_16($str) {
07060             return pack('H*', md5($str));
07061         }
07062         
07072         protected function _Ovalue($user_pass, $owner_pass) {
07073             $tmp = $this->_md5_16($owner_pass);
07074             $owner_RC4_key = substr($tmp, 0, 5);
07075             return $this->_RC4($owner_RC4_key, $user_pass);
07076         }
07077         
07085         protected function _Uvalue() {
07086             return $this->_RC4($this->encryption_key, $this->padding);
07087         }
07088         
07098         protected function _generateencryptionkey($user_pass, $owner_pass, $protection) {
07099             // Pad passwords
07100             $user_pass = substr($user_pass.$this->padding, 0, 32);
07101             $owner_pass = substr($owner_pass.$this->padding, 0, 32);
07102             // Compute O value
07103             $this->Ovalue = $this->_Ovalue($user_pass, $owner_pass);
07104             // Compute encyption key
07105             $tmp = $this->_md5_16($user_pass.$this->Ovalue.chr($protection)."\xFF\xFF\xFF");
07106             $this->encryption_key = substr($tmp, 0, 5);
07107             // Compute U value
07108             $this->Uvalue = $this->_Uvalue();
07109             // Compute P value
07110             $this->Pvalue = -(($protection^255) + 1);
07111         }
07112         
07130         public function SetProtection($permissions=array(), $user_pass='', $owner_pass=null) {
07131             $options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'annot-forms' => 32);
07132             $protection = 192;
07133             foreach ($permissions as $permission) {
07134                 if (!isset($options[$permission])) {
07135                     $this->Error('Incorrect permission: '.$permission);
07136                 }
07137                 $protection += $options[$permission];
07138             }
07139             if ($owner_pass === null) {
07140                 $owner_pass = uniqid(rand());
07141             }
07142             $this->encrypted = true;
07143             $this->_generateencryptionkey($user_pass, $owner_pass, $protection);
07144         }
07145         
07146         // END OF ENCRYPTION FUNCTIONS -------------------------
07147         
07148         // START TRANSFORMATIONS SECTION -----------------------
07149         
07158         public function StartTransform() {
07159             $this->_out('q');
07160             $this->transfmrk[$this->page][] = $this->pagelen[$this->page];
07161         }
07162         
07171         public function StopTransform() {
07172             $this->_out('Q');
07173             if (isset($this->transfmatrix)) {
07174                 array_pop($this->transfmatrix);
07175             }
07176             array_pop($this->transfmrk[$this->page]);
07177         }
07187         public function ScaleX($s_x, $x='', $y='') {
07188             $this->Scale($s_x, 100, $x, $y);
07189         }
07190         
07200         public function ScaleY($s_y, $x='', $y='') {
07201             $this->Scale(100, $s_y, $x, $y);
07202         }
07203         
07213         public function ScaleXY($s, $x='', $y='') {
07214             $this->Scale($s, $s, $x, $y);
07215         }
07216         
07227         public function Scale($s_x, $s_y, $x='', $y='') {
07228             if ($x === '') {
07229                 $x = $this->x;
07230             }
07231             if ($y === '') {
07232                 $y = $this->y;
07233             }
07234             if ($this->rtl) {
07235                 $x = $this->w - $x;
07236             }
07237             if (($s_x == 0) OR ($s_y == 0)) {
07238                 $this->Error('Please do not use values equal to zero for scaling');
07239             }
07240             $y = ($this->h - $y) * $this->k;
07241             $x *= $this->k;
07242             //calculate elements of transformation matrix
07243             $s_x /= 100;
07244             $s_y /= 100;
07245             $tm[0] = $s_x;
07246             $tm[1] = 0;
07247             $tm[2] = 0;
07248             $tm[3] = $s_y;
07249             $tm[4] = $x * (1 - $s_x);
07250             $tm[5] = $y * (1 - $s_y);
07251             //scale the coordinate system
07252             $this->Transform($tm);
07253         }
07254         
07262         public function MirrorH($x='') {
07263             $this->Scale(-100, 100, $x);
07264         }
07265         
07273         public function MirrorV($y='') {
07274             $this->Scale(100, -100, '', $y);
07275         }
07276         
07285         public function MirrorP($x='',$y='') {
07286             $this->Scale(-100, -100, $x, $y);
07287         }
07288         
07298         public function MirrorL($angle=0, $x='',$y='') {
07299             $this->Scale(-100, 100, $x, $y);
07300             $this->Rotate(-2*($angle-90), $x, $y);
07301         }
07302         
07310         public function TranslateX($t_x) {
07311             $this->Translate($t_x, 0);
07312         }
07313         
07321         public function TranslateY($t_y) {
07322             $this->Translate(0, $t_y);
07323         }
07324         
07333         public function Translate($t_x, $t_y) {
07334             if ($this->rtl) {
07335                 $t_x = -$t_x;
07336             }
07337             //calculate elements of transformation matrix
07338             $tm[0] = 1;
07339             $tm[1] = 0;
07340             $tm[2] = 0;
07341             $tm[3] = 1;
07342             $tm[4] = $t_x * $this->k;
07343             $tm[5] = -$t_y * $this->k;
07344             //translate the coordinate system
07345             $this->Transform($tm);
07346         }
07347         
07357         public function Rotate($angle, $x='', $y='') {
07358             if ($x === '') {
07359                 $x = $this->x;
07360             }
07361             if ($y === '') {
07362                 $y = $this->y;
07363             }
07364             if ($this->rtl) {
07365                 $x = $this->w - $x;
07366                 $angle = -$angle;
07367             }
07368             $y = ($this->h - $y) * $this->k;
07369             $x *= $this->k;
07370             //calculate elements of transformation matrix
07371             $tm[0] = cos(deg2rad($angle));
07372             $tm[1] = sin(deg2rad($angle));
07373             $tm[2] = -$tm[1];
07374             $tm[3] = $tm[0];
07375             $tm[4] = $x + ($tm[1] * $y) - ($tm[0] * $x);
07376             $tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x);
07377             //rotate the coordinate system around ($x,$y)
07378             $this->Transform($tm);
07379         }
07380         
07390         public function SkewX($angle_x, $x='', $y='') {
07391             $this->Skew($angle_x, 0, $x, $y);
07392         }
07393         
07403         public function SkewY($angle_y, $x='', $y='') {
07404             $this->Skew(0, $angle_y, $x, $y);
07405         }
07406         
07417         public function Skew($angle_x, $angle_y, $x='', $y='') {
07418             if ($x === '') {
07419                 $x = $this->x;
07420             }
07421             if ($y === '') {
07422                 $y = $this->y;
07423             }
07424             if ($this->rtl) {
07425                 $x = $this->w - $x;
07426                 $angle_x = -$angle_x;
07427             }
07428             if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) {
07429                 $this->Error('Please use values between -90 and +90 degrees for Skewing.');
07430             }
07431             $x *= $this->k;
07432             $y = ($this->h - $y) * $this->k;
07433             //calculate elements of transformation matrix
07434             $tm[0] = 1;
07435             $tm[1] = tan(deg2rad($angle_y));
07436             $tm[2] = tan(deg2rad($angle_x));
07437             $tm[3] = 1;
07438             $tm[4] = -$tm[2] * $y;
07439             $tm[5] = -$tm[1] * $x;
07440             //skew the coordinate system
07441             $this->Transform($tm);
07442         }
07443         
07450         protected function Transform($tm) {
07451             $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
07452             // store transformation matrix
07453             $this->transfmatrix[] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]);
07454             // update tranformation mark
07455             if (end($this->transfmrk[$this->page]) !== false) {
07456                 $key = key($this->transfmrk[$this->page]);
07457                 $this->transfmrk[$this->page][$key] = $this->pagelen[$this->page];
07458             }
07459         }
07460         
07461         // END TRANSFORMATIONS SECTION -------------------------
07462         
07463         
07464         // START GRAPHIC FUNCTIONS SECTION ---------------------
07465         // The following section is based on the code provided by David Hernandez Sanz
07466         
07474         public function SetLineWidth($width) {
07475             //Set line width
07476             $this->LineWidth = $width;
07477             $this->linestyleWidth = sprintf('%.2F w', ($width * $this->k));
07478             $this->_out($this->linestyleWidth);
07479         }
07480         
07488         public function GetLineWidth() {
07489             return $this->LineWidth;
07490         }
07491         
07513         public function SetLineStyle($style) {
07514             extract($style);
07515             if (isset($width)) {
07516                 $width_prev = $this->LineWidth;
07517                 $this->SetLineWidth($width);
07518                 $this->LineWidth = $width_prev;
07519             }
07520             if (isset($cap)) {
07521                 $ca = array('butt' => 0, 'round'=> 1, 'square' => 2);
07522                 if (isset($ca[$cap])) {
07523                     $this->linestyleCap = $ca[$cap].' J';
07524                     $this->_out($this->linestyleCap);
07525                 }
07526             }
07527             if (isset($join)) {
07528                 $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
07529                 if (isset($ja[$join])) {
07530                     $this->linestyleJoin = $ja[$join].' j';
07531                     $this->_out($this->linestyleJoin);
07532                 }
07533             }
07534             if (isset($dash)) {
07535                 $dash_string = '';
07536                 if ($dash) {
07537                     if (ereg('^.+,', $dash)) {
07538                         $tab = explode(',', $dash);
07539                     } else {
07540                         $tab = array($dash);
07541                     }
07542                     $dash_string = '';
07543                     foreach ($tab as $i => $v) {
07544                         if ($i) {
07545                             $dash_string .= ' ';
07546                         }
07547                         $dash_string .= sprintf("%.2F", $v);
07548                     }
07549                 }
07550                 if (!isset($phase) OR !$dash) {
07551                     $phase = 0;
07552                 }
07553                 $this->linestyleDash = sprintf("[%s] %.2F d", $dash_string, $phase);
07554                 $this->_out($this->linestyleDash);
07555             }
07556             if (isset($color)) {
07557                 $this->SetDrawColorArray($color);
07558             }
07559         }
07560         
07561         /*
07562         * Set a draw point.
07563         * @param float $x Abscissa of point.
07564         * @param float $y Ordinate of point.
07565         * @access protected
07566         * @since 2.1.000 (2008-01-08)
07567         */
07568         protected function _outPoint($x, $y) {
07569             if ($this->rtl) {
07570                 $x = $this->w - $x;
07571             }
07572             $this->_out(sprintf("%.2F %.2F m", $x * $this->k, ($this->h - $y) * $this->k));
07573         }
07574         
07575         /*
07576         * Draws a line from last draw point.
07577         * @param float $x Abscissa of end point.
07578         * @param float $y Ordinate of end point.
07579         * @access protected
07580         * @since 2.1.000 (2008-01-08)
07581         */
07582         protected function _outLine($x, $y) {
07583             if ($this->rtl) {
07584                 $x = $this->w - $x;
07585             }
07586             $this->_out(sprintf("%.2F %.2F l", $x * $this->k, ($this->h - $y) * $this->k));
07587         }
07588         
07599         protected function _outRect($x, $y, $w, $h, $op) {
07600             if ($this->rtl) {
07601                 $x = $this->w - $x - $w;
07602             }
07603             $this->_out(sprintf('%.2F %.2F %.2F %.2F re %s', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k, $op));
07604         }
07605         
07606         /*
07607         * Draws a Bezier curve from last draw point.
07608         * The Bezier curve is a tangent to the line between the control points at either end of the curve.
07609         * @param float $x1 Abscissa of control point 1.
07610         * @param float $y1 Ordinate of control point 1.
07611         * @param float $x2 Abscissa of control point 2.
07612         * @param float $y2 Ordinate of control point 2.
07613         * @param float $x3 Abscissa of end point.
07614         * @param float $y3 Ordinate of end point.
07615         * @access protected
07616         * @since 2.1.000 (2008-01-08)
07617         */
07618         protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) {
07619             if ($this->rtl) {
07620                 $x1 = $this->w - $x1;
07621                 $x2 = $this->w - $x2;
07622                 $x3 = $this->w - $x3;
07623             }
07624             $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));
07625         }
07626         
07638         public function Line($x1, $y1, $x2, $y2, $style=array()) {
07639             if ($style) {
07640                 $this->SetLineStyle($style);
07641             }
07642             $this->_outPoint($x1, $y1);
07643             $this->_outLine($x2, $y2);
07644             $this->_out(' S');
07645         }
07646         
07673         public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) {
07674             if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
07675                 $this->SetFillColorArray($fill_color);
07676             }
07677             switch ($style) {
07678                 case 'F': {
07679                     $op = 'f';
07680                     $border_style = array();
07681                     $this->_outRect($x, $y, $w, $h, $op);
07682                     break;
07683                 }
07684                 case 'DF':
07685                 case 'FD': {
07686                     if ((!$border_style) OR (isset($border_style['all']))) {
07687                         $op = 'B';
07688                         if (isset($border_style['all'])) {
07689                             $this->SetLineStyle($border_style['all']);
07690                             $border_style = array();
07691                         }
07692                     } else {
07693                         $op = 'f';
07694                     }
07695                     $this->_outRect($x, $y, $w, $h, $op);
07696                     break;
07697                 }
07698                 case 'CNZ': {
07699                     $op = 'W n';
07700                     $this->_outRect($x, $y, $w, $h, $op);
07701                     break;
07702                 }
07703                 case 'CEO': {
07704                     $op = 'W* n';
07705                     $this->_outRect($x, $y, $w, $h, $op);
07706                     break;
07707                 }
07708                 default: {
07709                     $op = 'S';
07710                     if ((!$border_style) OR (isset($border_style['all']))) {
07711                         if (isset($border_style['all']) AND $border_style['all']) {
07712                             $this->SetLineStyle($border_style['all']);
07713                             $border_style = array();
07714                         }
07715                         $this->_outRect($x, $y, $w, $h, $op);
07716                     }
07717                     break;
07718                 }
07719             }
07720             if ($border_style) {
07721                 $border_style2 = array();
07722                 foreach ($border_style as $line => $value) {
07723                     $lenght = strlen($line);
07724                     for ($i = 0; $i < $lenght; ++$i) {
07725                         $border_style2[$line[$i]] = $value;
07726                     }
07727                 }
07728                 $border_style = $border_style2;
07729                 if (isset($border_style['L']) AND $border_style['L']) {
07730                     $this->Line($x, $y, $x, $y + $h, $border_style['L']);
07731                 }
07732                 if (isset($border_style['T']) AND $border_style['T']) {
07733                     $this->Line($x, $y, $x + $w, $y, $border_style['T']);
07734                 }
07735                 if (isset($border_style['R']) AND $border_style['R']) {
07736                     $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style['R']);
07737                 }
07738                 if (isset($border_style['B']) AND $border_style['B']) {
07739                     $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style['B']);
07740                 }
07741             }
07742         }
07743         
07744         
07771         public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) {
07772             if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
07773                 $this->SetFillColorArray($fill_color);
07774             }
07775             switch ($style) {
07776                 case 'F': {
07777                     $op = 'f';
07778                     $line_style = array();
07779                     break;
07780                 }
07781                 case 'FD': 
07782                 case 'DF': {
07783                     $op = 'B';
07784                     break;
07785                 }
07786                 case 'CNZ': {
07787                     $op = 'W n';
07788                     break;
07789                 }
07790                 case 'CEO': {
07791                     $op = 'W* n';
07792                     break;
07793                 }
07794                 default: {
07795                     $op = 'S';
07796                     break;
07797                 }
07798             }
07799             if ($line_style) {
07800                 $this->SetLineStyle($line_style);
07801             }
07802             $this->_outPoint($x0, $y0);
07803             $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
07804             $this->_out($op);
07805         }
07806         
07828         public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) {
07829             if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
07830                 $this->SetFillColorArray($fill_color);
07831             }
07832             switch ($style) {
07833                 case 'F': {
07834                     $op = 'f';
07835                     $line_style = array();
07836                     break;
07837                 }
07838                 case 'FD':
07839                 case 'DF': {
07840                     $op = 'B';
07841                     break;
07842                 }
07843                 case 'CNZ': {
07844                     $op = 'W n';
07845                     break;
07846                 }
07847                 case 'CEO': {
07848                     $op = 'W* n';
07849                     break;
07850                 }
07851                 default: {
07852                     $op = 'S';
07853                     break;
07854                 }
07855             }
07856             if ($line_style) {
07857                 $this->SetLineStyle($line_style);
07858             }
07859             $this->_outPoint($x0, $y0);
07860             foreach ($segments as $segment) {
07861                 list($x1, $y1, $x2, $y2, $x3, $y3) = $segment;
07862                 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
07863             }   
07864             $this->_out($op);
07865         }
07866         
07892         public function Ellipse($x0, $y0, $rx, $ry=0, $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=8) {
07893             if ($angle) {
07894                 $this->StartTransform();
07895                 $this->Rotate($angle, $x0, $y0);
07896                 $this->Ellipse($x0, $y0, $rx, $ry, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc);
07897                 $this->StopTransform();
07898                 return;
07899             }
07900             if ($rx) {
07901                 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
07902                     $this->SetFillColorArray($fill_color);
07903                 }
07904                 switch ($style) {
07905                     case 'F': {
07906                         $op = 'f';
07907                         $line_style = array();
07908                         break;
07909                     }
07910                     case 'FD': 
07911                     case 'DF': {
07912                         $op = 'B';
07913                         break;
07914                     }
07915                     case 'C': {
07916                         $op = 's'; // Small 's' signifies closing the path as well
07917                         break;
07918                     }
07919                     case 'CNZ': {
07920                         $op = 'W n';
07921                         break;
07922                     }
07923                     case 'CEO': {
07924                         $op = 'W* n';
07925                         break;
07926                     }
07927                     default: {
07928                         $op = 'S';
07929                         break;
07930                     }
07931                 }
07932                 if ($line_style) {
07933                     $this->SetLineStyle($line_style);
07934                 }
07935                 if (!$ry) {
07936                     $ry = $rx;
07937                 }
07938                 $rx *= $this->k;
07939                 $ry *= $this->k;
07940                 if ($nc < 2) {
07941                     $nc = 2;
07942                 }
07943                 $astart = deg2rad((float) $astart);
07944                 $afinish = deg2rad((float) $afinish);
07945                 $total_angle = $afinish - $astart;
07946                 $dt = $total_angle / $nc;
07947                 $dtm = $dt / 3;
07948                 $x0 *= $this->k;
07949                 $y0 = ($this->h - $y0) * $this->k;
07950                 $t1 = $astart;
07951                 $a0 = $x0 + ($rx * cos($t1));
07952                 $b0 = $y0 + ($ry * sin($t1));
07953                 $c0 = -$rx * sin($t1);
07954                 $d0 = $ry * cos($t1);
07955                 $this->_outPoint($a0 / $this->k, $this->h - ($b0 / $this->k));
07956                 for ($i = 1; $i <= $nc; ++$i) {
07957                     // Draw this bit of the total curve
07958                     $t1 = ($i * $dt) + $astart;
07959                     $a1 = $x0 + ($rx * cos($t1));
07960                     $b1 = $y0 + ($ry * sin($t1));
07961                     $c1 = -$rx * sin($t1);
07962                     $d1 = $ry * cos($t1);
07963                     $this->_outCurve(($a0 + ($c0 * $dtm)) / $this->k, $this->h - (($b0 + ($d0 * $dtm)) / $this->k), ($a1 - ($c1 * $dtm)) / $this->k, $this->h - (($b1 - ($d1 * $dtm)) / $this->k), $a1 / $this->k, $this->h - ($b1 / $this->k));
07964                     $a0 = $a1;
07965                     $b0 = $b1;
07966                     $c0 = $c1;
07967                     $d0 = $d1;
07968                 }
07969                 $this->_out($op);
07970             }
07971         }
07972         
07996         public function Circle($x0, $y0, $r, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=8) {
07997             $this->Ellipse($x0, $y0, $r, 0, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc);
07998         }
07999         
08021         public function Polygon($p, $style='', $line_style=array(), $fill_color=array()) {
08022             $np = count($p) / 2;
08023             if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
08024                 $this->SetFillColorArray($fill_color);
08025             }
08026             switch ($style) {
08027                 case 'F': {
08028                     $line_style = array();
08029                     $op = 'f';
08030                     break;
08031                 }
08032                 case 'FD': 
08033                 case 'DF': {
08034                     $op = 'B';
08035                     break;
08036                 }
08037                 case 'CNZ': {
08038                     $op = 'W n';
08039                     break;
08040                 }
08041                 case 'CEO': {
08042                     $op = 'W* n';
08043                     break;
08044                 }               
08045                 default: {
08046                     $op = 'S';
08047                     break;
08048                 }
08049             }
08050             $draw = true;
08051             if ($line_style) {
08052                 if (isset($line_style['all'])) {
08053                     $this->SetLineStyle($line_style['all']);
08054                 } else { // 0 .. (np - 1), op = {B, S}
08055                     $draw = false;
08056                     if ('B' == $op) {
08057                         $op = 'f';
08058                         $this->_outPoint($p[0], $p[1]);
08059                         for ($i = 2; $i < ($np * 2); $i = $i + 2) {
08060                             $this->_outLine($p[$i], $p[$i + 1]);
08061                         }
08062                         $this->_outLine($p[0], $p[1]);
08063                         $this->_out($op);
08064                     }
08065                     $p[($np * 2)] = $p[0];
08066                     $p[(($np * 2) + 1)] = $p[1];
08067                     for ($i = 0; $i < $np; ++$i) {
08068                         if (isset($line_style[$i]) AND ($line_style[$i] != 0)) {
08069                             $this->Line($p[($i * 2)], $p[(($i * 2) + 1)], $p[(($i * 2) + 2)], $p[(($i * 2) + 3)], $line_style[$i]);
08070                         }
08071                     }
08072                 }
08073             }
08074             if ($draw) {
08075                 $this->_outPoint($p[0], $p[1]);
08076                 for ($i = 2; $i < ($np * 2); $i = $i + 2) {
08077                     $this->_outLine($p[$i], $p[$i + 1]);
08078                 }
08079                 $this->_outLine($p[0], $p[1]);
08080                 $this->_out($op);
08081             }
08082         }
08083         
08120         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()) {
08121             if (3 > $ns) {
08122                 $ns = 3;
08123             }
08124             if ($draw_circle) {
08125                 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
08126             }
08127             $p = array();
08128             for ($i = 0; $i < $ns; ++$i) {
08129                 $a = $angle + ($i * 360 / $ns);
08130                 $a_rad = deg2rad((float) $a);
08131                 $p[] = $x0 + ($r * sin($a_rad));
08132                 $p[] = $y0 + ($r * cos($a_rad));
08133             }
08134             $this->Polygon($p, $style, $line_style, $fill_color);
08135         }
08136         
08175         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()) {
08176             if (2 > $nv) {
08177                 $nv = 2;
08178             }
08179             if ($draw_circle) {
08180                 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
08181             }
08182             $p2 = array();
08183             $visited = array();
08184             for ($i = 0; $i < $nv; ++$i) {
08185                 $a = $angle + ($i * 360 / $nv);
08186                 $a_rad = deg2rad((float) $a);
08187                 $p2[] = $x0 + ($r * sin($a_rad));
08188                 $p2[] = $y0 + ($r * cos($a_rad));
08189                 $visited[] = false;
08190             }
08191             $p = array();
08192             $i = 0;
08193             do {
08194                 $p[] = $p2[$i * 2];
08195                 $p[] = $p2[($i * 2) + 1];
08196                 $visited[$i] = true;
08197                 $i += $ng;
08198                 $i %= $nv;
08199             } while (!$visited[$i]);
08200             $this->Polygon($p, $style, $line_style, $fill_color);
08201         }
08202         
08224         public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
08225             if ('0000' == $round_corner) { // Not rounded
08226                 $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
08227             } else { // Rounded
08228                 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
08229                     $this->SetFillColorArray($fill_color);
08230                 }
08231                 switch ($style) {
08232                     case 'F': {
08233                         $border_style = array();
08234                         $op = 'f';
08235                         break;
08236                     }
08237                     case 'FD': 
08238                     case 'DF': {
08239                         $op = 'B';
08240                         break;
08241                     }
08242                     case 'CNZ': {
08243                         $op = 'W n';
08244                         break;
08245                     }
08246                     case 'CEO': {
08247                         $op = 'W* n';
08248                         break;
08249                     }
08250                     default: {
08251                         $op = 'S';
08252                         break;
08253                     }
08254                 }
08255                 if ($border_style) {
08256                     $this->SetLineStyle($border_style);
08257                 }
08258                 $MyArc = 4 / 3 * (sqrt(2) - 1);
08259                 $this->_outPoint($x + $r, $y);
08260                 $xc = $x + $w - $r;
08261                 $yc = $y + $r;
08262                 $this->_outLine($xc, $y);
08263                 if ($round_corner[0]) {
08264                     $this->_outCurve($xc + ($r * $MyArc), $yc - $r, $xc + $r, $yc - ($r * $MyArc), $xc + $r, $yc);
08265                 } else {
08266                     $this->_outLine($x + $w, $y);
08267                 }
08268                 $xc = $x + $w - $r;
08269                 $yc = $y + $h - $r;
08270                 $this->_outLine($x + $w, $yc);
08271                 if ($round_corner[1]) {
08272                     $this->_outCurve($xc + $r, $yc + ($r * $MyArc), $xc + ($r * $MyArc), $yc + $r, $xc, $yc + $r);
08273                 } else {
08274                     $this->_outLine($x + $w, $y + $h);
08275                 }
08276                 $xc = $x + $r;
08277                 $yc = $y + $h - $r;
08278                 $this->_outLine($xc, $y + $h);
08279                 if ($round_corner[2]) {
08280                     $this->_outCurve($xc - ($r * $MyArc), $yc + $r, $xc - $r, $yc + ($r * $MyArc), $xc - $r, $yc);
08281                 } else {
08282                     $this->_outLine($x, $y + $h);
08283                 }
08284                 $xc = $x + $r;
08285                 $yc = $y + $r;
08286                 $this->_outLine($x, $yc);
08287                 if ($round_corner[3]) {
08288                     $this->_outCurve($xc - $r, $yc - ($r * $MyArc), $xc - ($r * $MyArc), $yc - $r, $xc, $yc - $r);
08289                 } else {
08290                     $this->_outLine($x, $y);
08291                     $this->_outLine($x + $r, $y);
08292                 }
08293                 $this->_out($op);
08294             }
08295         }
08296         
08297         // END GRAPHIC FUNCTIONS SECTION -----------------------
08298         
08299         // BIDIRECTIONAL TEXT SECTION --------------------------
08309         protected function utf8StrRev($str, $setbom=false, $forcertl=false) {
08310             return $this->arrUTF8ToUTF16BE($this->utf8Bidi($this->UTF8StringToArray($str), $str, $forcertl), $setbom);
08311         }
08312         
08323         protected function utf8Bidi($ta, $str='', $forcertl=false) {
08324             global $unicode, $unicode_mirror, $unicode_arlet, $laa_array, $diacritics;
08325             // paragraph embedding level
08326             $pel = 0;
08327             // max level
08328             $maxlevel = 0;
08329             if ($this->empty_string($str)) {
08330                 // create string from array
08331                 $str = $this->UTF8ArrSubString($ta);
08332             }
08333             // check if string contains arabic text
08334             if (preg_match(K_RE_PATTERN_ARABIC, $str)) {
08335                 $arabic = true;
08336             } else {
08337                 $arabic = false;
08338             }
08339             // check if string contains RTL text
08340             if (!($forcertl OR $arabic OR preg_match(K_RE_PATTERN_RTL, $str))) {
08341                 return $ta;
08342             }
08343             
08344             // get number of chars
08345             $numchars = count($ta);
08346             
08347             if ($forcertl == 'R') {
08348                     $pel = 1;
08349             } elseif ($forcertl == 'L') {
08350                     $pel = 0;
08351             } else {
08352                 // P2. In each paragraph, find the first character of type L, AL, or R.
08353                 // 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.
08354                 for ($i=0; $i < $numchars; ++$i) {
08355                     $type = $unicode[$ta[$i]];
08356                     if ($type == 'L') {
08357                         $pel = 0;
08358                         break;
08359                     } elseif (($type == 'AL') OR ($type == 'R')) {
08360                         $pel = 1;
08361                         break;
08362                     }
08363                 }
08364             }
08365             
08366             // Current Embedding Level
08367             $cel = $pel;
08368             // directional override status
08369             $dos = 'N';
08370             $remember = array();
08371             // start-of-level-run
08372             $sor = $pel % 2 ? 'R' : 'L';
08373             $eor = $sor;
08374             
08375             // Array of characters data
08376             $chardata = Array();
08377             
08378             // 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.
08379             //  In the resolution of levels in rules I1 and I2, the maximum embedding level of 62 can be reached.
08380             for ($i=0; $i < $numchars; ++$i) {
08381                 if ($ta[$i] == K_RLE) {
08382                     // X2. With each RLE, compute the least greater odd embedding level.
08383                     //  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.
08384                     //  b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
08385                     $next_level = $cel + ($cel % 2) + 1;
08386                     if ($next_level < 62) {
08387                         $remember[] = array('num' => K_RLE, 'cel' => $cel, 'dos' => $dos);
08388                         $cel = $next_level;
08389                         $dos = 'N';
08390                         $sor = $eor;
08391                         $eor = $cel % 2 ? 'R' : 'L';
08392                     }
08393                 } elseif ($ta[$i] == K_LRE) {
08394                     // X3. With each LRE, compute the least greater even embedding level.
08395                     //  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.
08396                     //  b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
08397                     $next_level = $cel + 2 - ($cel % 2);
08398                     if ( $next_level < 62 ) {
08399                         $remember[] = array('num' => K_LRE, 'cel' => $cel, 'dos' => $dos);
08400                         $cel = $next_level;
08401                         $dos = 'N';
08402                         $sor = $eor;
08403                         $eor = $cel % 2 ? 'R' : 'L';
08404                     }
08405                 } elseif ($ta[$i] == K_RLO) {
08406                     // X4. With each RLO, compute the least greater odd embedding level.
08407                     //  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.
08408                     //  b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
08409                     $next_level = $cel + ($cel % 2) + 1;
08410                     if ($next_level < 62) {
08411                         $remember[] = array('num' => K_RLO, 'cel' => $cel, 'dos' => $dos);
08412                         $cel = $next_level;
08413                         $dos = 'R';
08414                         $sor = $eor;
08415                         $eor = $cel % 2 ? 'R' : 'L';
08416                     }
08417                 } elseif ($ta[$i] == K_LRO) {
08418                     // X5. With each LRO, compute the least greater even embedding level.
08419                     //  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.
08420                     //  b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
08421                     $next_level = $cel + 2 - ($cel % 2);
08422                     if ( $next_level < 62 ) {
08423                         $remember[] = array('num' => K_LRO, 'cel' => $cel, 'dos' => $dos);
08424                         $cel = $next_level;
08425                         $dos = 'L';
08426                         $sor = $eor;
08427                         $eor = $cel % 2 ? 'R' : 'L';
08428                     }
08429                 } elseif ($ta[$i] == K_PDF) {
08430                     // 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.
08431                     if (count($remember)) {
08432                         $last = count($remember ) - 1;
08433                         if (($remember[$last]['num'] == K_RLE) OR 
08434                               ($remember[$last]['num'] == K_LRE) OR 
08435                               ($remember[$last]['num'] == K_RLO) OR 
08436                               ($remember[$last]['num'] == K_LRO)) {
08437                             $match = array_pop($remember);
08438                             $cel = $match['cel'];
08439                             $dos = $match['dos'];
08440                             $sor = $eor;
08441                             $eor = ($cel > $match['cel'] ? $cel : $match['cel']) % 2 ? 'R' : 'L';
08442                         }
08443                     }
08444                 } elseif (($ta[$i] != K_RLE) AND
08445                                  ($ta[$i] != K_LRE) AND
08446                                  ($ta[$i] != K_RLO) AND
08447                                  ($ta[$i] != K_LRO) AND
08448                                  ($ta[$i] != K_PDF)) {
08449                     // X6. For all types besides RLE, LRE, RLO, LRO, and PDF:
08450                     //  a. Set the level of the current character to the current embedding level.
08451                     //  b. Whenever the directional override status is not neutral, reset the current character type to the directional override status.
08452                     if ($dos != 'N') {
08453                         $chardir = $dos;
08454                     } else {
08455                         $chardir = $unicode[$ta[$i]];
08456                     }
08457                     // stores string characters and other information
08458                     $chardata[] = array('char' => $ta[$i], 'level' => $cel, 'type' => $chardir, 'sor' => $sor, 'eor' => $eor);
08459                 }
08460             } // end for each char
08461             
08462             // X8. All explicit directional embeddings and overrides are completely terminated at the end of each paragraph. Paragraph separators are not included in the embedding.
08463             // X9. Remove all RLE, LRE, RLO, LRO, PDF, and BN codes.
08464             // 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.
08465             
08466             // 3.3.3 Resolving Weak Types
08467             // 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.
08468             // Nonspacing marks are now resolved based on the previous characters.
08469             $numchars = count($chardata);
08470             
08471             // 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.
08472             $prevlevel = -1; // track level changes
08473             $levcount = 0; // counts consecutive chars at the same level
08474             for ($i=0; $i < $numchars; ++$i) {
08475                 if ($chardata[$i]['type'] == 'NSM') {
08476                     if ($levcount) {
08477                         $chardata[$i]['type'] = $chardata[$i]['sor'];
08478                     } elseif ($i > 0) {
08479                         $chardata[$i]['type'] = $chardata[($i-1)]['type'];
08480                     }
08481                 }
08482                 if ($chardata[$i]['level'] != $prevlevel) {
08483                     $levcount = 0;
08484                 } else {
08485                     ++$levcount;
08486                 }
08487                 $prevlevel = $chardata[$i]['level'];
08488             }
08489             
08490             // 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.
08491             $prevlevel = -1;
08492             $levcount = 0;
08493             for ($i=0; $i < $numchars; ++$i) {
08494                 if ($chardata[$i]['char'] == 'EN') {
08495                     for ($j=$levcount; $j >= 0; $j--) {
08496                         if ($chardata[$j]['type'] == 'AL') {
08497                             $chardata[$i]['type'] = 'AN';
08498                         } elseif (($chardata[$j]['type'] == 'L') OR ($chardata[$j]['type'] == 'R')) {
08499                             break;
08500                         }
08501                     }
08502                 }
08503                 if ($chardata[$i]['level'] != $prevlevel) {
08504                     $levcount = 0;
08505                 } else {
08506                     ++$levcount;
08507                 }
08508                 $prevlevel = $chardata[$i]['level'];
08509             }
08510             
08511             // W3. Change all ALs to R.
08512             for ($i=0; $i < $numchars; ++$i) {
08513                 if ($chardata[$i]['type'] == 'AL') {
08514                     $chardata[$i]['type'] = 'R';
08515                 } 
08516             }
08517             
08518             // 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.
08519             $prevlevel = -1;
08520             $levcount = 0;
08521             for ($i=0; $i < $numchars; ++$i) {
08522                 if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
08523                     if (($chardata[$i]['type'] == 'ES') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
08524                         $chardata[$i]['type'] = 'EN';
08525                     } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
08526                         $chardata[$i]['type'] = 'EN';
08527                     } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'AN') AND ($chardata[($i+1)]['type'] == 'AN')) {
08528                         $chardata[$i]['type'] = 'AN';
08529                     }
08530                 }
08531                 if ($chardata[$i]['level'] != $prevlevel) {
08532                     $levcount = 0;
08533                 } else {
08534                     ++$levcount;
08535                 }
08536                 $prevlevel = $chardata[$i]['level'];
08537             }
08538             
08539             // W5. A sequence of European terminators adjacent to European numbers changes to all European numbers.
08540             $prevlevel = -1;
08541             $levcount = 0;
08542             for ($i=0; $i < $numchars; ++$i) {
08543                 if ($chardata[$i]['type'] == 'ET') {
08544                     if (($levcount > 0) AND ($chardata[($i-1)]['type'] == 'EN')) {
08545                         $chardata[$i]['type'] = 'EN';
08546                     } else {
08547                         $j = $i+1;
08548                         while (($j < $numchars) AND ($chardata[$j]['level'] == $prevlevel)) {
08549                             if ($chardata[$j]['type'] == 'EN') {
08550                                 $chardata[$i]['type'] = 'EN';
08551                                 break;
08552                             } elseif ($chardata[$j]['type'] != 'ET') {
08553                                 break;
08554                             }
08555                             ++$j;
08556                         }
08557                     }
08558                 }
08559                 if ($chardata[$i]['level'] != $prevlevel) {
08560                     $levcount = 0;
08561                 } else {
08562                     ++$levcount;
08563                 }
08564                 $prevlevel = $chardata[$i]['level'];
08565             }
08566             
08567             // W6. Otherwise, separators and terminators change to Other Neutral.
08568             $prevlevel = -1;
08569             $levcount = 0;
08570             for ($i=0; $i < $numchars; ++$i) {
08571                 if (($chardata[$i]['type'] == 'ET') OR ($chardata[$i]['type'] == 'ES') OR ($chardata[$i]['type'] == 'CS')) {
08572                     $chardata[$i]['type'] = 'ON';
08573                 }
08574                 if ($chardata[$i]['level'] != $prevlevel) {
08575                     $levcount = 0;
08576                 } else {
08577                     ++$levcount;
08578                 }
08579                 $prevlevel = $chardata[$i]['level'];
08580             }
08581             
08582             //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.
08583             $prevlevel = -1;
08584             $levcount = 0;
08585             for ($i=0; $i < $numchars; ++$i) {
08586                 if ($chardata[$i]['char'] == 'EN') {
08587                     for ($j=$levcount; $j >= 0; $j--) {
08588                         if ($chardata[$j]['type'] == 'L') {
08589                             $chardata[$i]['type'] = 'L';
08590                         } elseif ($chardata[$j]['type'] == 'R') {
08591                             break;
08592                         }
08593                     }
08594                 }
08595                 if ($chardata[$i]['level'] != $prevlevel) {
08596                     $levcount = 0;
08597                 } else {
08598                     ++$levcount;
08599                 }
08600                 $prevlevel = $chardata[$i]['level'];
08601             }
08602             
08603             // 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.
08604             $prevlevel = -1;
08605             $levcount = 0;
08606             for ($i=0; $i < $numchars; ++$i) {
08607                 if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
08608                     if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
08609                         $chardata[$i]['type'] = 'L';
08610                     } elseif (($chardata[$i]['type'] == 'N') AND
08611                      (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
08612                      (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
08613                         $chardata[$i]['type'] = 'R';
08614                     } elseif ($chardata[$i]['type'] == 'N') {
08615                         // N2. Any remaining neutrals take the embedding direction
08616                         $chardata[$i]['type'] = $chardata[$i]['sor'];
08617                     }
08618                 } elseif (($levcount == 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
08619                     // first char
08620                     if (($chardata[$i]['type'] == 'N') AND ($chardata[$i]['sor'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
08621                         $chardata[$i]['type'] = 'L';
08622                     } elseif (($chardata[$i]['type'] == 'N') AND
08623                      (($chardata[$i]['sor'] == 'R') OR ($chardata[$i]['sor'] == 'EN') OR ($chardata[$i]['sor'] == 'AN')) AND
08624                      (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
08625                         $chardata[$i]['type'] = 'R';
08626                     } elseif ($chardata[$i]['type'] == 'N') {
08627                         // N2. Any remaining neutrals take the embedding direction
08628                         $chardata[$i]['type'] = $chardata[$i]['sor'];
08629                     }
08630                 } elseif (($levcount > 0) AND ((($i+1) == $numchars) OR (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] != $prevlevel))) {
08631                     //last char
08632                     if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[$i]['eor'] == 'L')) {
08633                         $chardata[$i]['type'] = 'L';
08634                     } elseif (($chardata[$i]['type'] == 'N') AND
08635                      (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
08636                      (($chardata[$i]['eor'] == 'R') OR ($chardata[$i]['eor'] == 'EN') OR ($chardata[$i]['eor'] == 'AN'))) {
08637                         $chardata[$i]['type'] = 'R';
08638                     } elseif ($chardata[$i]['type'] == 'N') {
08639                         // N2. Any remaining neutrals take the embedding direction
08640                         $chardata[$i]['type'] = $chardata[$i]['sor'];
08641                     }
08642                 } elseif ($chardata[$i]['type'] == 'N') {
08643                     // N2. Any remaining neutrals take the embedding direction
08644                     $chardata[$i]['type'] = $chardata[$i]['sor'];
08645                 }
08646                 if ($chardata[$i]['level'] != $prevlevel) {
08647                     $levcount = 0;
08648                 } else {
08649                     ++$levcount;
08650                 }
08651                 $prevlevel = $chardata[$i]['level'];
08652             }
08653             
08654             // 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.
08655             // I2. For all characters with an odd (right-to-left) embedding direction, those of type L, EN or AN go up one level.
08656             for ($i=0; $i < $numchars; ++$i) {
08657                 $odd = $chardata[$i]['level'] % 2;
08658                 if ($odd) {
08659                     if (($chardata[$i]['type'] == 'L') OR ($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')) {
08660                         $chardata[$i]['level'] += 1;
08661                     }
08662                 } else {
08663                     if ($chardata[$i]['type'] == 'R') {
08664                         $chardata[$i]['level'] += 1;
08665                     } elseif (($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')) {
08666                         $chardata[$i]['level'] += 2;
08667                     }
08668                 }
08669                 $maxlevel = max($chardata[$i]['level'],$maxlevel);
08670             }
08671             
08672             // L1. On each line, reset the embedding level of the following characters to the paragraph embedding level:
08673             //  1. Segment separators,
08674             //  2. Paragraph separators,
08675             //  3. Any sequence of whitespace characters preceding a segment separator or paragraph separator, and
08676             //  4. Any sequence of white space characters at the end of the line.
08677             for ($i=0; $i < $numchars; ++$i) {
08678                 if (($chardata[$i]['type'] == 'B') OR ($chardata[$i]['type'] == 'S')) {
08679                     $chardata[$i]['level'] = $pel;
08680                 } elseif ($chardata[$i]['type'] == 'WS') {
08681                     $j = $i+1;
08682                     while ($j < $numchars) {
08683                         if ((($chardata[$j]['type'] == 'B') OR ($chardata[$j]['type'] == 'S')) OR
08684                             (($j == ($numchars-1)) AND ($chardata[$j]['type'] == 'WS'))) {
08685                             $chardata[$i]['level'] = $pel;
08686                             break;
08687                         } elseif ($chardata[$j]['type'] != 'WS') {
08688                             break;
08689                         }
08690                         ++$j;
08691                     }
08692                 }
08693             }
08694             
08695             // Arabic Shaping
08696             // 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. 
08697             if ($arabic) {
08698                 $endedletter = array(1569,1570,1571,1572,1573,1575,1577,1583,1584,1585,1586,1608,1688);
08699                 $alfletter = array(1570,1571,1573,1575);
08700                 $chardata2 = $chardata;
08701                 $laaletter = false;
08702                 $charAL = array();
08703                 $x = 0;
08704                 for ($i=0; $i < $numchars; ++$i) {
08705                     if (($unicode[$chardata[$i]['char']] == 'AL') OR ($chardata[$i]['char'] == 32) OR ($chardata[$i]['char'] == 8204)) {
08706                         $charAL[$x] = $chardata[$i];
08707                         $charAL[$x]['i'] = $i;
08708                         $chardata[$i]['x'] = $x;
08709                         ++$x;
08710                     }
08711                 }
08712                 $numAL = $x;
08713                 for ($i=0; $i < $numchars; ++$i) {
08714                     $thischar = $chardata[$i];
08715                     if ($i > 0) {
08716                         $prevchar = $chardata[($i-1)];
08717                     } else {
08718                         $prevchar = false;
08719                     }
08720                     if (($i+1) < $numchars) {
08721                         $nextchar = $chardata[($i+1)];
08722                     } else {
08723                         $nextchar = false;
08724                     }
08725                     if ($unicode[$thischar['char']] == 'AL') {
08726                         $x = $thischar['x'];
08727                         if ($x > 0) {
08728                             $prevchar = $charAL[($x-1)];
08729                         } else {
08730                             $prevchar = false;
08731                         }
08732                         if (($x+1) < $numAL) {
08733                             $nextchar = $charAL[($x+1)];
08734                         } else {
08735                             $nextchar = false;
08736                         }
08737                         // if laa letter
08738                         if (($prevchar !== false) AND ($prevchar['char'] == 1604) AND (in_array($thischar['char'], $alfletter))) {
08739                             $arabicarr = $laa_array;
08740                             $laaletter = true;
08741                             if ($x > 1) {
08742                                 $prevchar = $charAL[($x-2)];
08743                             } else {
08744                                 $prevchar = false;
08745                             }
08746                         } else {
08747                             $arabicarr = $unicode_arlet;
08748                             $laaletter = false;
08749                         }
08750                         if (($prevchar !== false) AND ($nextchar !== false) AND
08751                             (($unicode[$prevchar['char']] == 'AL') OR ($unicode[$prevchar['char']] == 'NSM')) AND
08752                             (($unicode[$nextchar['char']] == 'AL') OR ($unicode[$nextchar['char']] == 'NSM')) AND
08753                             ($prevchar['type'] == $thischar['type']) AND
08754                             ($nextchar['type'] == $thischar['type']) AND
08755                             ($nextchar['char'] != 1567)) {
08756                             if (in_array($prevchar['char'], $endedletter)) {
08757                                 if (isset($arabicarr[$thischar['char']][2])) {
08758                                     // initial
08759                                     $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
08760                                 }
08761                             } else {
08762                                 if (isset($arabicarr[$thischar['char']][3])) {
08763                                     // medial
08764                                     $chardata2[$i]['char'] = $arabicarr[$thischar['char']][3];
08765                                 }
08766                             }
08767                         } elseif (($nextchar !== false) AND
08768                             (($unicode[$nextchar['char']] == 'AL') OR ($unicode[$nextchar['char']] == 'NSM')) AND
08769                             ($nextchar['type'] == $thischar['type']) AND
08770                             ($nextchar['char'] != 1567)) {
08771                             if (isset($arabicarr[$chardata[$i]['char']][2])) {
08772                                 // initial
08773                                 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
08774                             }
08775                         } elseif ((($prevchar !== false) AND
08776                             (($unicode[$prevchar['char']] == 'AL') OR ($unicode[$prevchar['char']] == 'NSM')) AND
08777                             ($prevchar['type'] == $thischar['type'])) OR
08778                             (($nextchar !== false) AND ($nextchar['char'] == 1567))) {
08779                             // final
08780                             if (($i > 1) AND ($thischar['char'] == 1607) AND
08781                                 ($chardata[$i-1]['char'] == 1604) AND
08782                                 ($chardata[$i-2]['char'] == 1604)) {
08783                                 //Allah Word
08784                                 // mark characters to delete with false
08785                                 $chardata2[$i-2]['char'] = false;
08786                                 $chardata2[$i-1]['char'] = false; 
08787                                 $chardata2[$i]['char'] = 65010;
08788                             } else {
08789                                 if (($prevchar !== false) AND in_array($prevchar['char'], $endedletter)) {
08790                                     if (isset($arabicarr[$thischar['char']][0])) {
08791                                         // isolated
08792                                         $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
08793                                     }
08794                                 } else {
08795                                     if (isset($arabicarr[$thischar['char']][1])) {
08796                                         // final
08797                                         $chardata2[$i]['char'] = $arabicarr[$thischar['char']][1];
08798                                     }
08799                                 }
08800                             }
08801                         } elseif (isset($arabicarr[$thischar['char']][0])) {
08802                             // isolated
08803                             $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
08804                         }
08805                         // if laa letter
08806                         if ($laaletter) {
08807                             // mark characters to delete with false
08808                             $chardata2[($charAL[($x-1)]['i'])]['char'] = false;
08809                         }
08810                     } // end if AL (Arabic Letter)
08811                 } // end for each char
08812                 /* 
08813                  * Combining characters that can occur with Shadda (0651 HEX, 1617 DEC) are placed in UE586-UE594. 
08814                  * Putting the combining mark and shadda in the same glyph allows us to avoid the two marks overlapping each other in an illegible manner.
08815                  */
08816                 $cw = &$this->CurrentFont['cw'];
08817                 for ($i = 0; $i < ($numchars-1); ++$i) {
08818                     if (($chardata2[$i]['char'] == 1617) AND (isset($diacritics[($chardata2[$i+1]['char'])]))) {
08819                         // check if the subtitution font is defined on current font
08820                         if (isset($cw[($diacritics[($chardata2[$i+1]['char'])])])) {
08821                             $chardata2[$i]['char'] = false;
08822                             $chardata2[$i+1]['char'] = $diacritics[($chardata2[$i+1]['char'])];
08823                         }
08824                     }
08825                 }
08826                 // remove marked characters
08827                 foreach ($chardata2 as $key => $value) {
08828                     if ($value['char'] === false) {
08829                         unset($chardata2[$key]);
08830                     }
08831                 }
08832                 $chardata = array_values($chardata2);
08833                 $numchars = count($chardata);
08834                 unset($chardata2);
08835                 unset($arabicarr);
08836                 unset($laaletter);
08837                 unset($charAL);
08838             }
08839             
08840             // 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.
08841             for ($j=$maxlevel; $j > 0; $j--) {
08842                 $ordarray = Array();
08843                 $revarr = Array();
08844                 $onlevel = false;
08845                 for ($i=0; $i < $numchars; ++$i) {
08846                     if ($chardata[$i]['level'] >= $j) {
08847                         $onlevel = true;
08848                         if (isset($unicode_mirror[$chardata[$i]['char']])) {
08849                             // 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.
08850                             $chardata[$i]['char'] = $unicode_mirror[$chardata[$i]['char']];
08851                         }
08852                         $revarr[] = $chardata[$i];
08853                     } else {
08854                         if ($onlevel) {
08855                             $revarr = array_reverse($revarr);
08856                             $ordarray = array_merge($ordarray, $revarr);
08857                             $revarr = Array();
08858                             $onlevel = false;
08859                         }
08860                         $ordarray[] = $chardata[$i];
08861                     }
08862                 }
08863                 if ($onlevel) {
08864                     $revarr = array_reverse($revarr);
08865                     $ordarray = array_merge($ordarray, $revarr);
08866                 }
08867                 $chardata = $ordarray;
08868             }
08869             
08870             $ordarray = array();
08871             for ($i=0; $i < $numchars; ++$i) {
08872                 $ordarray[] = $chardata[$i]['char'];
08873             }
08874             
08875             return $ordarray;
08876         }
08877         
08878         // END OF BIDIRECTIONAL TEXT SECTION -------------------
08879         
08880         /*
08881         * Adds a bookmark.
08882         * @param string $txt bookmark description.
08883         * @param int $level bookmark level (minimum value is 0).
08884         * @param float $y Ordinate of the boorkmark position (default = -1 = current position).
08885         * @param int $page target page number (leave empty for current page).
08886         * @access public
08887         * @author Olivier Plathey, Nicola Asuni
08888         * @since 2.1.002 (2008-02-12)
08889         */
08890         public function Bookmark($txt, $level=0, $y=-1, $page='') {
08891             if ($level < 0) {
08892                 $level = 0;
08893             }
08894             if (isset($this->outlines[0])) {
08895                 $lastoutline = end($this->outlines);
08896                 $maxlevel = $lastoutline['l'] + 1;
08897             } else {
08898                 $maxlevel = 0;
08899             }
08900             if ($level > $maxlevel) {
08901                 $level = $maxlevel;
08902             }
08903             if ($y == -1) {
08904                 $y = $this->GetY();
08905             }
08906             if (empty($page)) {
08907                 $page = $this->PageNo();
08908             }
08909             $this->outlines[] = array('t' => $txt, 'l' => $level, 'y' => $y, 'p' => $page);
08910         }
08911         
08912         /*
08913         * Create a bookmark PDF string.
08914         * @access protected
08915         * @author Olivier Plathey, Nicola Asuni
08916         * @since 2.1.002 (2008-02-12)
08917         */
08918         protected function _putbookmarks() {
08919             $nb = count($this->outlines);
08920             if ($nb == 0) {
08921                 return;
08922             }
08923             $lru = array();
08924             $level = 0;
08925             foreach ($this->outlines as $i => $o) {
08926                 if ($o['l'] > 0) {
08927                     $parent = $lru[($o['l'] - 1)];
08928                     //Set parent and last pointers
08929                     $this->outlines[$i]['parent'] = $parent;
08930                     $this->outlines[$parent]['last'] = $i;
08931                     if ($o['l'] > $level) {
08932                         //Level increasing: set first pointer
08933                         $this->outlines[$parent]['first'] = $i;
08934                     }
08935                 } else {
08936                     $this->outlines[$i]['parent'] = $nb;
08937                 }
08938                 if (($o['l'] <= $level) AND ($i > 0)) {
08939                     //Set prev and next pointers
08940                     $prev = $lru[$o['l']];
08941                     $this->outlines[$prev]['next'] = $i;
08942                     $this->outlines[$i]['prev'] = $prev;
08943                 }
08944                 $lru[$o['l']] = $i;
08945                 $level = $o['l'];
08946             }
08947             //Outline items
08948             $n = $this->n + 1;
08949             foreach ($this->outlines as $i => $o) {
08950                 $this->_newobj();
08951                 $this->_out('<</Title '.$this->_textstring($o['t']));
08952                 $this->_out('/Parent '.($n + $o['parent']).' 0 R');
08953                 if (isset($o['prev']))
08954                 $this->_out('/Prev '.($n + $o['prev']).' 0 R');
08955                 if (isset($o['next']))
08956                 $this->_out('/Next '.($n + $o['next']).' 0 R');
08957                 if (isset($o['first']))
08958                 $this->_out('/First '.($n + $o['first']).' 0 R');
08959                 if (isset($o['last']))
08960                 $this->_out('/Last '.($n + $o['last']).' 0 R');
08961                 $this->_out(sprintf('/Dest [%d 0 R /XYZ 0 %.2F null]', (1 + (2 * $o['p'])), ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k))));
08962                 $this->_out('/Count 0>>');
08963                 $this->_out('endobj');
08964             }
08965             //Outline root
08966             $this->_newobj();
08967             $this->OutlineRoot = $this->n;
08968             $this->_out('<</Type /Outlines /First '.$n.' 0 R');
08969             $this->_out('/Last '.($n + $lru[0]).' 0 R>>');
08970             $this->_out('endobj');
08971         }
08972         
08973         
08974         // --- JAVASCRIPT - FORMS ------------------------------
08975         
08976         /*
08977         * Adds a javascript
08978         * @access public
08979         * @author Johannes Güntert, Nicola Asuni
08980         * @since 2.1.002 (2008-02-12)
08981         */
08982         public function IncludeJS($script) {
08983             $this->javascript .= $script;
08984         }
08985         
08986         /*
08987         * Create a javascript PDF string.
08988         * @access protected
08989         * @author Johannes Güntert, Nicola Asuni
08990         * @since 2.1.002 (2008-02-12)
08991         */
08992         protected function _putjavascript() {
08993             if (empty($this->javascript)) {
08994                 return;
08995             }
08996             // the following two lines are uded to avoid form fields duplication after saving
08997             $js1 = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%.2F,%.2F,%.2F,%.2F]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1);
08998             $js2 = "getField('tcpdfdocsaved').value = 'saved';";
08999             $this->_newobj();
09000             $this->n_js = $this->n;
09001             $this->_out('<<');
09002             $this->_out('/Names [(EmbeddedJS) '.($this->n + 1).' 0 R ]');
09003             $this->_out('>>');
09004             $this->_out('endobj');
09005             $this->_newobj();
09006             $this->_out('<<');
09007             $this->_out('/S /JavaScript');
09008             $this->_out('/JS '.$this->_textstring($js1."\n".$this->javascript."\n".$js2));
09009             $this->_out('>>');
09010             $this->_out('endobj');
09011         }
09012         
09013         /*
09014         * Convert color to javascript color.
09015         * @param string $color color name or #RRGGBB
09016         * @access protected
09017         * @author Denis Van Nuffelen, Nicola Asuni
09018         * @since 2.1.002 (2008-02-12)
09019         */
09020         protected function _JScolor($color) {
09021             static $aColors = array('transparent', 'black', 'white', 'red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'dkGray', 'gray', 'ltGray');
09022             if (substr($color,0,1) == '#') {
09023                 return sprintf("['RGB',%.3F,%.3F,%.3F]", hexdec(substr($color,1,2))/255, hexdec(substr($color,3,2))/255, hexdec(substr($color,5,2))/255);
09024             }
09025             if (!in_array($color,$aColors)) {
09026                 $this->Error('Invalid color: '.$color);
09027             }
09028             return 'color.'.$color;
09029         }
09030         
09031         /*
09032         * Adds a javascript form field.
09033         * @param string $type field type
09034         * @param string $name field name
09035         * @param int $x horizontal position
09036         * @param int $y vertical position
09037         * @param int $w width
09038         * @param int $h height
09039         * @param array $prop array of properties. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul>
09040         * @access protected
09041         * @author Denis Van Nuffelen, Nicola Asuni
09042         * @since 2.1.002 (2008-02-12)
09043         */
09044         protected function _addfield($type, $name, $x, $y, $w, $h, $prop) {
09045             if ($this->rtl) {
09046                 $x = $x - $w;
09047             }
09048             // the followind avoid fields duplication after saving the document
09049             $this->javascript .= "if(getField('tcpdfdocsaved').value != 'saved') {";
09050             $k = $this->k;
09051             $this->javascript .= sprintf("f".$name."=this.addField('%s','%s',%d,[%.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";
09052             $this->javascript .= 'f'.$name.'.textSize='.$this->FontSizePt.";\n";
09053             while (list($key, $val) = each($prop)) {
09054                 if (strcmp(substr($key, -5), 'Color') == 0) {
09055                     $val = $this->_JScolor($val);
09056                 } else {
09057                     $val = "'".$val."'";
09058                 }
09059                 $this->javascript .= 'f'.$name.'.'.$key.'='.$val.";\n";
09060             }
09061             if ($this->rtl) {
09062                 $this->x -= $w;
09063             } else {
09064                 $this->x += $w;
09065             }
09066             $this->javascript .= '}';
09067         }
09068         
09069         /*
09070         * Creates a text field
09071         * @param string $name field name
09072         * @param int $w width
09073         * @param int $h height
09074         * @param string $prop properties. The value property allows to set the initial value. The multiline property allows to define the field as multiline. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul>
09075         * @access public
09076         * @author Denis Van Nuffelen, Nicola Asuni
09077         * @since 2.1.002 (2008-02-12)
09078         */
09079         public function TextField($name, $w, $h, $prop=array()) {
09080             $this->_addfield('text', $name, $this->x, $this->y, $w, $h, $prop);
09081         }
09082         
09083         /*
09084         * Creates a RadioButton field
09085         * @param string $name field name
09086         * @param int $w width
09087         * @param string $prop properties. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul>
09088         * @access public
09089         * @author Nicola Asuni
09090         * @since 2.2.003 (2008-03-03)
09091         */
09092         public function RadioButton($name, $w, $prop=array()) {
09093             if (!isset($prop['strokeColor'])) {
09094                 $prop['strokeColor']='black';
09095             }
09096             $this->_addfield('radiobutton', $name, $this->x, $this->y, $w, $w, $prop);
09097         }
09098         
09099         /*
09100         * Creates a List-box field
09101         * @param string $name field name
09102         * @param int $w width
09103         * @param int $h height
09104         * @param array $values array containing the list of values.
09105         * @param string $prop properties. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul>
09106         * @access public
09107         * @author Nicola Asuni
09108         * @since 2.2.003 (2008-03-03)
09109         */
09110         public function ListBox($name, $w, $h, $values, $prop=array()) {
09111             if (!isset($prop['strokeColor'])) {
09112                 $prop['strokeColor'] = 'ltGray';
09113             }
09114             $this->_addfield('listbox', $name, $this->x, $this->y, $w, $h, $prop);
09115             $s = '';
09116             foreach ($values as $value) {
09117                 $s .= "'".addslashes($value)."',";
09118             }
09119             $this->javascript .= 'f'.$name.'.setItems(['.substr($s, 0, -1)."]);\n";
09120         }
09121         
09122         /*
09123         * Creates a Combo-box field
09124         * @param string $name field name
09125         * @param int $w width
09126         * @param int $h height
09127         * @param array $values array containing the list of values.
09128         * @param string $prop properties. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul>
09129         * @access public
09130         * @author Denis Van Nuffelen, Nicola Asuni
09131         * @since 2.1.002 (2008-02-12)
09132         */
09133         public function ComboBox($name, $w, $h, $values, $prop=array()) {
09134             $this->_addfield('combobox', $name, $this->x, $this->y, $w, $h, $prop);
09135             $s = '';
09136             foreach ($values as $value) {
09137                 $s .= "'".addslashes($value)."',";
09138             }
09139             $this->javascript .= 'f'.$name.'.setItems(['.substr($s, 0, -1)."]);\n";
09140         }
09141         
09142         /*
09143         * Creates a CheckBox field
09144         * @param string $name field name
09145         * @param int $w width
09146         * @param boolean $checked define the initial state (default = false).
09147         * @param string $prop properties. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul>
09148         * @access public
09149         * @author Denis Van Nuffelen, Nicola Asuni
09150         * @since 2.1.002 (2008-02-12)
09151         */
09152         public function CheckBox($name, $w, $checked=false, $prop=array()) {
09153             $prop['value'] = ($checked ? 'Yes' : 'Off');
09154             if (!isset($prop['strokeColor'])) {
09155                 $prop['strokeColor'] = 'black';
09156             }
09157             $this->_addfield('checkbox', $name, $this->x, $this->y, $w, $w, $prop);
09158         }
09159         
09160         /*
09161         * Creates a button field
09162         * @param string $name field name
09163         * @param int $w width
09164         * @param int $h height
09165         * @param string $caption caption.
09166         * @param string $action action triggered by the button (JavaScript code).
09167         * @param string $prop properties. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul>
09168         * @access public
09169         * @author Denis Van Nuffelen, Nicola Asuni
09170         * @since 2.1.002 (2008-02-12)
09171         */
09172         public function Button($name, $w, $h, $caption, $action, $prop=array()) {
09173             if (!isset($prop['strokeColor'])) {
09174                 $prop['strokeColor'] = 'black';
09175             }
09176             if (!isset($prop['borderStyle'])) {
09177                 $prop['borderStyle'] = 'beveled';
09178             }
09179             $this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop);
09180             $this->javascript .= 'f'.$name.".buttonSetCaption('".addslashes($caption)."');\n";
09181             $this->javascript .= 'f'.$name.".setAction('MouseUp','".addslashes($action)."');\n";
09182             $this->javascript .= 'f'.$name.".highlight='push';\n";
09183             $this->javascript .= 'f'.$name.".print=false;\n";
09184         }
09185         
09186         // END JAVASCRIPT - FORMS ------------------------------
09187         
09188         /*
09189         * Enable Write permissions for PDF Reader.
09190         * WARNING: This works only using the Adobe private key with the setSignature() method.
09191         * @access protected
09192         * @author Nicola Asuni
09193         * @since 2.9.000 (2008-03-26)
09194         */
09195         protected function _putuserrights() {
09196             if ((!$this->sign) OR (isset($this->signature_data['cert_type']) AND ($this->signature_data['cert_type'] > 0))) {
09197                 return;
09198             }
09199             $this->_out('/Perms');
09200             $this->_out('<<');
09201             $this->_out('/UR3');
09202             $this->_out('<<');
09203             $this->_out('/Type/Sig');
09204             $this->_out('/Filter/Adobe.PPKLite');
09205             $this->_out('/SubFilter/adbe.pkcs7.detached');
09206             $this->_out('/ByteRange[0 ********** ********** **********]');
09207             $this->_out('/Contents<>'.str_repeat(' ', $this->signature_max_lenght));
09208             if ($this->ur) {
09209                 $this->_out('/Reference');
09210                 $this->_out('[');
09211                 $this->_out('<<');
09212                 $this->_out('/Type/SigRef');
09213                 $this->_out('/TransformMethod/UR3');
09214                 $this->_out('/TransformParams');
09215                 $this->_out('<<');
09216                 $this->_out('/Type/TransformParams');
09217                 $this->_out('/V/2.2');
09218                 if (!$this->empty_string($this->ur_document)) {
09219                     $this->_out('/Document['.$this->ur_document.']');
09220                 }
09221                 if (!$this->empty_string($this->ur_annots)) {
09222                     $this->_out('/Annots['.$this->ur_annots.']');
09223                 }
09224                 if (!$this->empty_string($this->ur_form)) {
09225                     $this->_out('/Form['.$this->ur_form.']');
09226                 }
09227                 if (!$this->empty_string($this->ur_signature)) {
09228                     $this->_out('/Signature['.$this->ur_signature.']');
09229                 }           
09230                 $this->_out('>>');
09231                 $this->_out('>>');
09232                 $this->_out(']');
09233             }
09234             $this->_out('/M '.$this->_datastring('D:'.date('YmdHisO')));
09235             $this->_out('>>');
09236             $this->_out('>>');
09237         }
09238         
09239         /*
09240         * Add certification signature (DocMDP)
09241         * @access protected
09242         * @author Nicola Asuni
09243         * @since 4.6.008 (2009-05-07)
09244         */
09245         protected function _putcertification() {
09246             if ((!$this->sign) OR (isset($this->signature_data['cert_type']) AND ($this->signature_data['cert_type'] <= 0))) {
09247                 return;
09248             }
09249             $this->_out('/Perms');
09250             $this->_out('<<');
09251             $this->_out('/DocMDP');
09252             $this->_out('<<');
09253             $this->_out('/Type/Sig');
09254             $this->_out('/Filter/Adobe.PPKLite');
09255             $this->_out('/SubFilter/adbe.pkcs7.detached');
09256             $this->_out('/ByteRange[0 ********** ********** **********]');
09257             $this->_out('/Contents<>'.str_repeat(' ', $this->signature_max_lenght));
09258             $this->_out('/Reference');
09259             $this->_out('[');
09260             $this->_out('<<');
09261             $this->_out('/Type/SigRef');
09262             $this->_out('/TransformMethod/DocMDP');
09263             $this->_out('/TransformParams');
09264             $this->_out('<<');
09265             $this->_out('/Type/TransformParams');
09266             $this->_out('/V/1.2');
09267             $this->_out('/P '.$this->signature_data['cert_type'].'');
09268             $this->_out('>>');
09269             $this->_out('>>');
09270             $this->_out(']');
09271             $this->_out('/M '.$this->_datastring('D:'.date('YmdHisO')));
09272             if (isset($this->signature_data['info']['Name']) AND !$this->empty_string($this->signature_data['info']['Name'])) {
09273                 $this->_out('/Name '.$this->_textstring($this->signature_data['info']['Name']).'');
09274             }
09275             if (isset($this->signature_data['info']['Location']) AND !$this->empty_string($this->signature_data['info']['Location'])) {
09276                 $this->_out('/Location '.$this->_textstring($this->signature_data['info']['Location']).'');
09277             }
09278             if (isset($this->signature_data['info']['Reason']) AND !$this->empty_string($this->signature_data['info']['Reason'])) {
09279                 $this->_out('/Reason '.$this->_textstring($this->signature_data['info']['Reason']).'');
09280             }
09281             if (isset($this->signature_data['info']['ContactInfo']) AND !$this->empty_string($this->signature_data['info']['ContactInfo'])) {
09282                 $this->_out('/ContactInfo '.$this->_textstring($this->signature_data['info']['ContactInfo']).'');
09283             }
09284             $this->_out('>>');
09285             $this->_out('>>');
09286         }
09287         
09288         /*
09289         * Set User's Rights for PDF Reader
09290         * WARNING: This should work only using the Adobe private key with the setSignature() method.
09291         * Check the PDF Reference 8.7.1 Transform Methods, 
09292         * Table 8.105 Entries in the UR transform parameters dictionary
09293         * @param boolean $enable if true enable user's rights on PDF reader
09294         * @param string $document Names specifying additional document-wide usage rights for the document. The only defined value is "/FullSave", which permits a user to save the document along with modified form and/or annotation data.
09295         * @param string $annots Names specifying additional annotation-related usage rights for the document. Valid names in PDF 1.5 and later are /Create/Delete/Modify/Copy/Import/Export, which permit the user to perform the named operation on annotations.
09296         * @param string $form Names specifying additional form-field-related usage rights for the document. Valid names are: /Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate 
09297         * @param string $signature Names specifying additional signature-related usage rights for the document. The only defined value is /Modify, which permits a user to apply a digital signature to an existing signature form field or clear a signed signature form field.
09298         * @access public
09299         * @author Nicola Asuni
09300         * @since 2.9.000 (2008-03-26)
09301         */
09302         public function setUserRights(
09303                 $enable=true, 
09304                 $document='/FullSave',
09305                 $annots='/Create/Delete/Modify/Copy/Import/Export',
09306                 $form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate',
09307                 $signature='/Modify') {
09308             $this->ur = $enable;
09309             $this->ur_document = $document;
09310             $this->ur_annots = $annots;
09311             $this->ur_form = $form;
09312             $this->ur_signature = $signature;
09313             if ($this->ur) {
09314                 $this->setSignature('', '', '', '', 0);
09315             }
09316         }
09317         
09318         /*
09319         * Enable document signature (requires the OpenSSL Library).
09320         * The digital signature improve document authenticity and integrity and allows o enable extra features on Acrobat Reader.
09321         * @param mixed $signing_cert signing certificate (string or filename prefixed with 'file://')
09322         * @param mixed $private_key private key (string or filename prefixed with 'file://')
09323         * @param string $private_key_password password
09324         * @param string $extracerts specifies the name of a file containing a bunch of extra certificates to include in the signature which can for example be used to help the recipient to verify the certificate that you used.
09325         * @param int $cert_type The access permissions granted for this document. Valid values shall be: 1 = No changes to the document shall be permitted; any change to the document shall invalidate the signature; 2 = Permitted changes shall be filling in forms, instantiating page templates, and signing; other changes shall invalidate the signature; 3 = Permitted changes shall be the same as for 2, as well as annotation creation, deletion, and modification; other changes shall invalidate the signature.
09326         * @parm array $info array of option information: Name, Location, Reason, ContactInfo.
09327         * @access public
09328         * @author Nicola Asuni
09329         * @since 4.6.005 (2009-04-24)
09330         */
09331         public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array()) {
09332             // to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.pem -out tcpdf.pem
09333             $this->sign = true;
09334             $this->signature_data = array();
09335             if (strlen($signing_cert) == 0) {
09336                 $signing_cert = 'file://'.dirname(__FILE__).'/tcpdf.pem';
09337             }
09338             if (strlen($private_key) == 0) {
09339                 $private_key = $signing_cert;
09340             }
09341             $this->signature_data['signcert'] = $signing_cert;
09342             $this->signature_data['privkey'] = $private_key;
09343             $this->signature_data['password'] = $private_key_password;
09344             $this->signature_data['extracerts'] = $extracerts;
09345             $this->signature_data['cert_type'] = $cert_type;
09346             $this->signature_data['info'] = array();
09347         }
09348         
09349         /*
09350         * Create a new page group.
09351         * NOTE: call this function before calling AddPage()
09352         * @param int $page starting group page (leave empty for next page).
09353         * @access public
09354         * @since 3.0.000 (2008-03-27)
09355         */
09356         public function startPageGroup($page='') {
09357             if (empty($page)) {
09358                 $page = $this->page + 1;
09359             }
09360             $this->newpagegroup[$page] = true;
09361         }
09362 
09371         public function AliasNbPages($alias='{nb}') {
09372             $this->AliasNbPages = $alias;
09373         }
09374         
09383         public function getAliasNbPages() {
09384             if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
09385                 return '{'.$this->AliasNbPages.'}';
09386             }
09387             return $this->AliasNbPages;
09388         }
09389 
09398         public function AliasNumPage($alias='{pnb}') {
09399             //Define an alias for total number of pages
09400             $this->AliasNumPage = $alias;
09401         }
09402         
09411         public function getAliasNumPage() {
09412             if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
09413                 return '{'.$this->AliasNumPage.'}';
09414             }
09415             return $this->AliasNumPage;
09416         }
09417         
09418         /*
09419         * Return the current page in the group.
09420         * @return current page in the group
09421         * @access public
09422         * @since 3.0.000 (2008-03-27)
09423         */
09424         public function getGroupPageNo() {
09425             return $this->pagegroups[$this->currpagegroup];
09426         }
09427 
09434         public function getGroupPageNoFormatted() {
09435             return $this->formatPageNumber($this->getGroupPageNo());
09436         }
09437         
09438         /*
09439          * Return the alias of the current page group
09440          * If the current font is unicode type, the returned string is surrounded by additional curly braces.
09441          * (will be replaced by the total number of pages in this group).
09442          * @return alias of the current page group
09443          * @access public
09444          * @since 3.0.000 (2008-03-27)
09445         */
09446         public function getPageGroupAlias() {
09447             if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
09448                 return '{'.$this->currpagegroup.'}';
09449             }
09450             return $this->currpagegroup;
09451         }
09452         
09453         /*
09454          * Return the alias for the page number on the current page group
09455          * If the current font is unicode type, the returned string is surrounded by additional curly braces.
09456          * (will be replaced by the total number of pages in this group).
09457          * @return alias of the current page group
09458          * @access public
09459          * @since 4.5.000 (2009-01-02)
09460         */
09461         public function getPageNumGroupAlias() {
09462             if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
09463                 return '{'.str_replace('{nb', '{pnb', $this->currpagegroup).'}';
09464             }
09465             return str_replace('{nb', '{pnb', $this->currpagegroup);
09466         }
09467 
09475         protected function formatPageNumber($num) {
09476             return number_format((float)$num, 0, '', '.');
09477         }
09478 
09487         protected function formatTOCPageNumber($num) {
09488             return number_format((float)$num, 0, '', '.');
09489         }
09490 
09497         public function PageNoFormatted() {
09498             return $this->formatPageNumber($this->PageNo());
09499         }
09500 
09501         /*
09502         * Put visibility settings.
09503         * @access protected
09504         * @since 3.0.000 (2008-03-27)
09505         */
09506         protected function _putocg() {
09507             $this->_newobj();
09508             $this->n_ocg_print = $this->n;
09509             $this->_out('<</Type /OCG /Name '.$this->_textstring('print'));
09510             $this->_out('/Usage <</Print <</PrintState /ON>> /View <</ViewState /OFF>>>>>>');
09511             $this->_out('endobj');
09512             $this->_newobj();
09513             $this->n_ocg_view=$this->n;
09514             $this->_out('<</Type /OCG /Name '.$this->_textstring('view'));
09515             $this->_out('/Usage <</Print <</PrintState /OFF>> /View <</ViewState /ON>>>>>>');
09516             $this->_out('endobj');
09517         }
09518         
09519         /*
09520         * Set the visibility of the successive elements.
09521         * This can be useful, for instance, to put a background 
09522         * image or color that will show on screen but won't print.
09523         * @param string $v visibility mode. Legal values are: all, print, screen.
09524         * @access public
09525         * @since 3.0.000 (2008-03-27)
09526         */
09527         public function setVisibility($v) {
09528             if ($this->openMarkedContent) {
09529                 // close existing open marked-content
09530                 $this->_out('EMC');
09531                 $this->openMarkedContent = false;
09532             }
09533             switch($v) {
09534                 case 'print': {
09535                     $this->_out('/OC /OC1 BDC');
09536                     $this->openMarkedContent = true;
09537                     break;
09538                 }
09539                 case 'screen': {
09540                     $this->_out('/OC /OC2 BDC');
09541                     $this->openMarkedContent = true;
09542                     break;
09543                 }
09544                 case 'all': {
09545                     $this->_out('');
09546                     break;
09547                 }
09548                 default: {
09549                     $this->Error('Incorrect visibility: '.$v);
09550                     break;
09551                 }
09552             }
09553             $this->visibility = $v;
09554         }
09555         
09556         /*
09557         * Add transparency parameters to the current extgstate
09558         * @param array $params parameters
09559         * @return the number of extgstates
09560         * @access protected
09561         * @since 3.0.000 (2008-03-27)
09562         */
09563         protected function addExtGState($parms) {
09564             $n = count($this->extgstates) + 1;
09565             $this->extgstates[$n]['parms'] = $parms;
09566             return $n;
09567         }
09568         
09569         /*
09570         * Add an extgstate
09571         * @param array $gs extgstate
09572         * @access protected
09573         * @since 3.0.000 (2008-03-27)
09574         */
09575         protected function setExtGState($gs) {
09576             $this->_out(sprintf('/GS%d gs', $gs));
09577         }
09578         
09579         /*
09580         * Put extgstates for object transparency
09581         * @param array $gs extgstate
09582         * @access protected
09583         * @since 3.0.000 (2008-03-27)
09584         */
09585         protected function _putextgstates() {
09586             $ne = count($this->extgstates);
09587             for ($i = 1; $i <= $ne; ++$i) {
09588                 $this->_newobj();
09589                 $this->extgstates[$i]['n'] = $this->n;
09590                 $this->_out('<</Type /ExtGState');
09591                 foreach ($this->extgstates[$i]['parms'] as $k => $v) {
09592                     $this->_out('/'.$k.' '.$v);
09593                 }
09594                 $this->_out('>>');
09595                 $this->_out('endobj');
09596             }
09597         }
09598         
09599         /*
09600         * Set alpha for stroking (CA) and non-stroking (ca) operations.
09601         * @param float $alpha real value from 0 (transparent) to 1 (opaque)
09602         * @param string $bm blend mode, one of the following: Normal, Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn, HardLight, SoftLight, Difference, Exclusion, Hue, Saturation, Color, Luminosity
09603         * @access public
09604         * @since 3.0.000 (2008-03-27)
09605         */
09606         public function setAlpha($alpha, $bm='Normal') {
09607             $gs = $this->addExtGState(array('ca' => $alpha, 'CA' => $alpha, 'BM' => '/'.$bm));
09608             $this->setExtGState($gs);
09609         }
09610 
09611         /*
09612         * Set the default JPEG compression quality (1-100)
09613         * @param int $quality JPEG quality, integer between 1 and 100
09614         * @access public
09615         * @since 3.0.000 (2008-03-27)
09616         */
09617         public function setJPEGQuality($quality) {
09618             if (($quality < 1) OR ($quality > 100)) {
09619                 $quality = 75;
09620             }
09621             $this->jpeg_quality = intval($quality);
09622         }
09623         
09624         /*
09625         * Set the default number of columns in a row for HTML tables.
09626         * @param int $cols number of columns
09627         * @access public
09628         * @since 3.0.014 (2008-06-04)
09629         */
09630         public function setDefaultTableColumns($cols=4) { 
09631             $this->default_table_columns = intval($cols); 
09632         }
09633         
09634         /*
09635         * Set the height of cell repect font height.
09636         * @param int $h cell proportion respect font height (typical value = 1.25).
09637         * @access public
09638         * @since 3.0.014 (2008-06-04)
09639         */
09640         public function setCellHeightRatio($h) { 
09641             $this->cell_height_ratio = $h; 
09642         }
09643         
09644         /*
09645         * return the height of cell repect font height.
09646         * @access public
09647         * @since 4.0.012 (2008-07-24)
09648         */
09649         public function getCellHeightRatio() { 
09650             return $this->cell_height_ratio; 
09651         }
09652         
09653         /*
09654         * Set the PDF version (check PDF reference for valid values).
09655         * Default value is 1.t
09656         * @access public
09657         * @since 3.1.000 (2008-06-09)
09658         */
09659         public function setPDFVersion($version='1.7') { 
09660             $this->PDFVersion = $version;
09661         }
09662         
09663         /*
09664         * Set the viewer preferences dictionary controlling the way the document is to be presented on the screen or in print.
09665         * (see Section 8.1 of PDF reference, "Viewer Preferences").
09666         * <ul>
09667         * <li>HideToolbar boolean (Optional) A flag specifying whether to hide the viewer application's tool bars when the document is active. Default value: false.</li>
09668         * <li>HideMenubar boolean (Optional) A flag specifying whether to hide the viewer application's menu bar when the document is active. Default value: false.</li>
09669         * <li>HideWindowUI boolean (Optional) A flag specifying whether to hide user interface elements in the document's window (such as scroll bars and navigation controls), leaving only the document's contents displayed. Default value: false.</li>
09670         * <li>FitWindow boolean (Optional) A flag specifying whether to resize the document's window to fit the size of the first displayed page. Default value: false.</li>
09671         * <li>CenterWindow boolean (Optional) A flag specifying whether to position the document's window in the center of the screen. Default value: false.</li>
09672         * <li>DisplayDocTitle boolean (Optional; PDF 1.4) A flag specifying whether the window's title bar should display the document title taken from the Title entry of the document information dictionary (see Section 10.2.1, "Document Information Dictionary"). If false, the title bar should instead display the name of the PDF file containing the document. Default value: false.</li>
09673         * <li>NonFullScreenPageMode name (Optional) The document's page mode, specifying how to display the document on exiting full-screen mode:<ul><li>UseNone Neither document outline nor thumbnail images visible</li><li>UseOutlines Document outline visible</li><li>UseThumbs Thumbnail images visible</li><li>UseOC Optional content group panel visible</li><ul>This entry is meaningful only if the value of the PageMode entry in the catalog dictionary (see Section 3.6.1, "Document Catalog") is FullScreen; it is ignored otherwise. Default value: UseNone.</li>
09674         * <li>ViewArea name (Optional; PDF 1.4) The name of the page boundary representing the area of a page to be displayed when viewing the document on the screen. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li>
09675         * <li>ViewClip name (Optional; PDF 1.4) The name of the page boundary to which the contents of a page are to be clipped when viewing the document on the screen. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li>
09676         * <li>PrintArea name (Optional; PDF 1.4) The name of the page boundary representing the area of a page to be rendered when printing the document. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li>
09677         * <li>PrintClip name (Optional; PDF 1.4) The name of the page boundary to which the contents of a page are to be clipped when printing the document. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li>
09678         * <li>PrintScaling name (Optional; PDF 1.6) The page scaling option to be selected when a print dialog is displayed for this document. Valid values are: <ul><li>None, which indicates that the print dialog should reflect no page scaling</li><li>AppDefault (default), which indicates that applications should use the current print scaling</li><ul></li>
09679         * <li>Duplex name (Optional; PDF 1.7) The paper handling option to use when printing the file from the print dialog. The following values are valid:<ul><li>Simplex - Print single-sided</li><li>DuplexFlipShortEdge - Duplex and flip on the short edge of the sheet</li><li>DuplexFlipLongEdge - Duplex and flip on the long edge of the sheet</li></ul>Default value: none</li>
09680         * <li>PickTrayByPDFSize boolean (Optional; PDF 1.7) A flag specifying whether the PDF page size is used to select the input paper tray. This setting influences only the preset values used to populate the print dialog presented by a PDF viewer application. If PickTrayByPDFSize is true, the check box in the print dialog associated with input paper tray is checked. Note: This setting has no effect on Mac OS systems, which do not provide the ability to pick the input tray by size.</li>
09681         * <li>PrintPageRange array (Optional; PDF 1.7) The page numbers used to initialize the print dialog box when the file is printed. The first page of the PDF file is denoted by 1. Each pair consists of the first and last pages in the sub-range. An odd number of integers causes this entry to be ignored. Negative numbers cause the entire array to be ignored. Default value: as defined by PDF viewer application</li>
09682         * <li>NumCopies integer (Optional; PDF 1.7) The number of copies to be printed when the print dialog is opened for this file. Supported values are the integers 2 through 5. Values outside this range are ignored. Default value: as defined by PDF viewer application, but typically 1</li>
09683         * </ul>
09684         * @param array $preferences array of options.
09685         * @author Nicola Asuni
09686         * @access public
09687         * @since 3.1.000 (2008-06-09)
09688         */
09689         public function setViewerPreferences($preferences) { 
09690             $this->viewer_preferences = $preferences;
09691         }
09692         
09706         public function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) {
09707             $this->Clip($x, $y, $w, $h);
09708             $this->Gradient(2, $col1, $col2, $coords);
09709         }
09710         
09724         public function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) {
09725             $this->Clip($x, $y, $w, $h);
09726             $this->Gradient(3, $col1, $col2, $coords);
09727         }
09728         
09746         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) {
09747             $this->Clip($x, $y, $w, $h);        
09748             $n = count($this->gradients) + 1;
09749             $this->gradients[$n]['type'] = 6; //coons patch mesh
09750             //check the coords array if it is the simple array or the multi patch array
09751             if (!isset($coords[0]['f'])) {
09752                 //simple array -> convert to multi patch array
09753                 if (!isset($col1[1])) {
09754                     $col1[1] = $col1[2] = $col1[0];
09755                 }
09756                 if (!isset($col2[1])) {
09757                     $col2[1] = $col2[2] = $col2[0];
09758                 }
09759                 if (!isset($col3[1])) {
09760                     $col3[1] = $col3[2] = $col3[0];
09761                 }
09762                 if (!isset($col4[1])) {
09763                     $col4[1] = $col4[2] = $col4[0];
09764                 }
09765                 $patch_array[0]['f'] = 0;
09766                 $patch_array[0]['points'] = $coords;
09767                 $patch_array[0]['colors'][0]['r'] = $col1[0];
09768                 $patch_array[0]['colors'][0]['g'] = $col1[1];
09769                 $patch_array[0]['colors'][0]['b'] = $col1[2];
09770                 $patch_array[0]['colors'][1]['r'] = $col2[0];
09771                 $patch_array[0]['colors'][1]['g'] = $col2[1];
09772                 $patch_array[0]['colors'][1]['b'] = $col2[2];
09773                 $patch_array[0]['colors'][2]['r'] = $col3[0];
09774                 $patch_array[0]['colors'][2]['g'] = $col3[1];
09775                 $patch_array[0]['colors'][2]['b'] = $col3[2];
09776                 $patch_array[0]['colors'][3]['r'] = $col4[0];
09777                 $patch_array[0]['colors'][3]['g'] = $col4[1];
09778                 $patch_array[0]['colors'][3]['b'] = $col4[2];
09779             } else {
09780                 //multi patch array
09781                 $patch_array = $coords;
09782             }
09783             $bpcd = 65535; //16 BitsPerCoordinate
09784             //build the data stream
09785             $this->gradients[$n]['stream'] = '';
09786             $count_patch = count($patch_array);
09787             for($i=0; $i < $count_patch; ++$i) {
09788                 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit
09789                 $count_points = count($patch_array[$i]['points']);
09790                 for($j=0; $j < $count_points; ++$j) {
09791                     //each point as 16 bit
09792                     $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j] - $coords_min) / ($coords_max - $coords_min)) * $bpcd;
09793                     if ($patch_array[$i]['points'][$j] < 0) {
09794                         $patch_array[$i]['points'][$j] = 0;
09795                     }
09796                     if ($patch_array[$i]['points'][$j] > $bpcd) {
09797                         $patch_array[$i]['points'][$j] = $bpcd;
09798                     }
09799                     $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] / 256));
09800                     $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] % 256));
09801                 }
09802                 $count_cols = count($patch_array[$i]['colors']);
09803                 for($j=0; $j < $count_cols; ++$j) {
09804                     //each color component as 8 bit
09805                     $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']);
09806                     $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']);
09807                     $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']);
09808                 }
09809             }
09810             //paint the gradient
09811             $this->_out('/Sh'.$n.' sh');
09812             //restore previous Graphic State
09813             $this->_out('Q');
09814         }
09815         
09826         protected function Clip($x, $y, $w, $h) {
09827             if ($this->rtl) {
09828                 $x = $this->w - $x - $w;
09829             }
09830             //save current Graphic State
09831             $s = 'q';
09832             //set clipping area
09833             $s .= sprintf(' %.2F %.2F %.2F %.2F re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k);
09834             //set up transformation matrix for gradient
09835             $s .= sprintf(' %.3F 0 0 %.3F %.3F %.3F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k);
09836             $this->_out($s);
09837         }
09838                 
09849         protected function Gradient($type, $col1, $col2, $coords) {
09850             $n = count($this->gradients) + 1;
09851             $this->gradients[$n]['type'] = $type;
09852             if (!isset($col1[1])) {
09853                 $col1[1]=$col1[2]=$col1[0];
09854             }
09855             $this->gradients[$n]['col1'] = sprintf('%.3F %.3F %.3F', ($col1[0]/255), ($col1[1]/255), ($col1[2]/255));
09856             if (!isset($col2[1])) {
09857                 $col2[1] = $col2[2] = $col2[0];
09858             }
09859             $this->gradients[$n]['col2'] = sprintf('%.3F %.3F %.3F', ($col2[0]/255), ($col2[1]/255), ($col2[2]/255));
09860             $this->gradients[$n]['coords'] = $coords;
09861             //paint the gradient
09862             $this->_out('/Sh'.$n.' sh');
09863             //restore previous Graphic State
09864             $this->_out('Q');
09865         }
09866         
09873         function _putshaders() {
09874             foreach ($this->gradients as $id => $grad) {  
09875                 if (($grad['type'] == 2) OR ($grad['type'] == 3)) {
09876                     $this->_newobj();
09877                     $this->_out('<<');
09878                     $this->_out('/FunctionType 2');
09879                     $this->_out('/Domain [0.0 1.0]');
09880                     $this->_out('/C0 ['.$grad['col1'].']');
09881                     $this->_out('/C1 ['.$grad['col2'].']');
09882                     $this->_out('/N 1');
09883                     $this->_out('>>');
09884                     $this->_out('endobj');
09885                     $f1 = $this->n;
09886                 }
09887                 $this->_newobj();
09888                 $this->_out('<<');
09889                 $this->_out('/ShadingType '.$grad['type']);
09890                 $this->_out('/ColorSpace /DeviceRGB');
09891                 if ($grad['type'] == 2) {
09892                     $this->_out(sprintf('/Coords [%.3F %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]));
09893                     $this->_out('/Function '.$f1.' 0 R');
09894                     $this->_out('/Extend [true true] ');
09895                     $this->_out('>>');
09896                 } elseif ($grad['type'] == 3) {
09897                     //x0, y0, r0, x1, y1, r1
09898                     //at this this time radius of inner circle is 0
09899                     $this->_out(sprintf('/Coords [%.3F %.3F 0 %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]));
09900                     $this->_out('/Function '.$f1.' 0 R');
09901                     $this->_out('/Extend [true true] ');
09902                     $this->_out('>>');
09903                 } elseif ($grad['type'] == 6) {
09904                     $this->_out('/BitsPerCoordinate 16');
09905                     $this->_out('/BitsPerComponent 8');
09906                     $this->_out('/Decode[0 1 0 1 0 1 0 1 0 1]');
09907                     $this->_out('/BitsPerFlag 8');
09908                     $this->_out('/Length '.strlen($grad['stream']));
09909                     $this->_out('>>');
09910                     $this->_putstream($grad['stream']);
09911                 }
09912                 $this->_out('endobj');
09913                 $this->gradients[$id]['id'] = $this->n;
09914             }
09915         }
09916 
09923         protected function _outarc($x1, $y1, $x2, $y2, $x3, $y3 ) {
09924             $h = $this->h;
09925             $this->_out(sprintf('%.2F %.2F %.2F %.2F %.2F %.2F c', $x1*$this->k, ($h-$y1)*$this->k, $x2*$this->k, ($h-$y2)*$this->k, $x3*$this->k, ($h-$y3)*$this->k));
09926         }
09927         
09943         public function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) {
09944             if ($this->rtl) {
09945                 $xc = $this->w - $xc;
09946             }
09947             if ($cw) {
09948                 $d = $b;
09949                 $b = $o - $a;
09950                 $a = $o - $d;
09951             } else {
09952                 $b += $o;
09953                 $a += $o;
09954             }
09955             $a = ($a % 360) + 360;
09956             $b = ($b % 360) + 360;
09957             if ($a > $b) {
09958                 $b +=360;
09959             }
09960             $b = $b / 360 * 2 * M_PI;
09961             $a = $a / 360 * 2 * M_PI;
09962             $d = $b - $a;
09963             if ($d == 0 ) {
09964                 $d = 2 * M_PI;
09965             }
09966             $k = $this->k;
09967             $hp = $this->h;
09968             if ($style=='F') {
09969                 $op = 'f';
09970             } elseif ($style=='FD' or $style=='DF') {
09971                 $op = 'b';
09972             } else {
09973                 $op = 's';
09974             }
09975             if (sin($d/2)) {
09976                 $MyArc = 4/3 * (1 - cos($d/2)) / sin($d/2) * $r;
09977             }
09978             //first put the center
09979             $this->_out(sprintf('%.2F %.2F m', ($xc)*$k, ($hp-$yc)*$k));
09980             //put the first point
09981             $this->_out(sprintf('%.2F %.2F l', ($xc+$r*cos($a))*$k, (($hp-($yc-$r*sin($a)))*$k)));
09982             //draw the arc
09983             if ($d < (M_PI/2)) {
09984                 $this->_outarc($xc+$r*cos($a)+$MyArc*cos(M_PI/2+$a), $yc-$r*sin($a)-$MyArc*sin(M_PI/2+$a), $xc+$r*cos($b)+$MyArc*cos($b-M_PI/2), $yc-$r*sin($b)-$MyArc*sin($b-M_PI/2), $xc+$r*cos($b), $yc-$r*sin($b));
09985             } else {
09986                 $b = $a + $d/4;
09987                 $MyArc = 4/3*(1-cos($d/8))/sin($d/8)*$r;
09988                 $this->_outarc($xc+$r*cos($a)+$MyArc*cos(M_PI/2+$a), $yc-$r*sin($a)-$MyArc*sin(M_PI/2+$a), $xc+$r*cos($b)+$MyArc*cos($b-M_PI/2), $yc-$r*sin($b)-$MyArc*sin($b-M_PI/2), $xc+$r*cos($b), $yc-$r*sin($b));
09989                 $a = $b;
09990                 $b = $a + $d/4;
09991                 $this->_outarc($xc+$r*cos($a)+$MyArc*cos(M_PI/2+$a), $yc-$r*sin($a)-$MyArc*sin(M_PI/2+$a), $xc+$r*cos($b)+$MyArc*cos($b-M_PI/2), $yc-$r*sin($b)-$MyArc*sin($b-M_PI/2), $xc+$r*cos($b), $yc-$r*sin($b));
09992                 $a = $b;
09993                 $b = $a + $d/4;
09994                 $this->_outarc($xc+$r*cos($a)+$MyArc*cos(M_PI/2+$a), $yc-$r*sin($a)-$MyArc*sin(M_PI/2+$a), $xc+$r*cos($b)+$MyArc*cos($b-M_PI/2), $yc-$r*sin($b)-$MyArc*sin($b-M_PI/2), $xc+$r*cos($b), $yc-$r*sin($b) );
09995                 $a = $b;
09996                 $b = $a + $d/4;
09997                 $this->_outarc($xc+$r*cos($a)+$MyArc*cos(M_PI/2+$a), $yc-$r*sin($a)-$MyArc*sin(M_PI/2+$a), $xc+$r*cos($b)+$MyArc*cos($b-M_PI/2), $yc-$r*sin($b)-$MyArc*sin($b-M_PI/2), $xc+$r*cos($b), $yc-$r*sin($b));
09998             }
09999             //terminate drawing
10000             $this->_out($op);
10001         }
10002         
10021         public function ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0) {
10022             if ($x === '') {
10023                 $x = $this->x;
10024             }
10025             if ($y === '') {
10026                 $y = $this->y;
10027             }
10028             $k = $this->k;
10029             $data = file_get_contents($file);
10030             if ($data === false) {
10031                 $this->Error('EPS file not found: '.$file);
10032             }
10033             $regs = array();
10034             // EPS/AI compatibility check (only checks files created by Adobe Illustrator!)
10035             preg_match("/%%Creator:([^\r\n]+)/", $data, $regs); # find Creator
10036             if (count($regs) > 1) {
10037                 $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0"
10038                 if (strpos($version_str, 'Adobe Illustrator') !== false) {
10039                     $versexp = explode(' ', $version_str);
10040                     $version = (float)array_pop($versexp);
10041                     if ($version >= 9) {
10042                         $this->Error('This version of Adobe Illustrator file is not supported: '.$file);
10043                     }
10044                 }
10045             }
10046             // strip binary bytes in front of PS-header
10047             $start = strpos($data, '%!PS-Adobe');
10048             if ($start > 0) {
10049                 $data = substr($data, $start);
10050             }
10051             // find BoundingBox params
10052             preg_match("/%%BoundingBox:([^\r\n]+)/", $data, $regs);
10053             if (count($regs) > 1) {
10054                 list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1]));
10055             } else {
10056                 $this->Error('No BoundingBox found in EPS file: '.$file);
10057             }
10058             $start = strpos($data, '%%EndSetup');
10059             if ($start === false) {
10060                 $start = strpos($data, '%%EndProlog');
10061             }
10062             if ($start === false) {
10063                 $start = strpos($data, '%%BoundingBox');
10064             }
10065             $data = substr($data, $start);
10066             $end = strpos($data, '%%PageTrailer');
10067             if ($end===false) {
10068                 $end = strpos($data, 'showpage');
10069             }
10070             if ($end) {
10071                 $data = substr($data, 0, $end);
10072             }
10073             if ($w > 0) {
10074                 $scale_x = $w / (($x2 - $x1) / $k);
10075                 if ($h > 0) {
10076                     $scale_y = $h / (($y2 - $y1) / $k);
10077                 } else {
10078                     $scale_y = $scale_x;
10079                     $h = ($y2 - $y1) / $k * $scale_y;
10080                 }
10081             } else {
10082                 if ($h > 0) {
10083                     $scale_y = $h / (($y2 - $y1) / $k);
10084                     $scale_x = $scale_y;
10085                     $w = ($x2-$x1) / $k * $scale_x;
10086                 } else {
10087                     $w = ($x2 - $x1) / $k;
10088                     $h = ($y2 - $y1) / $k;
10089                 }
10090             }
10091             // Check whether we need a new page first as this does not fit
10092             if ((($y + $h) > $this->PageBreakTrigger) AND (!$this->InFooter) AND $this->AcceptPageBreak()) {
10093                 // Automatic page break
10094                 $this->AddPage($this->CurOrientation);
10095                 // Reset Y coordinate to the top of next page
10096                 $y = $this->GetY() + $this->cMargin;
10097             }
10098             // set bottomcoordinates
10099             $this->img_rb_y = $y + $h;
10100             // set alignment
10101             if ($this->rtl) {
10102                 if ($palign == 'L') {
10103                     $ximg = $this->lMargin;
10104                     // set right side coordinate
10105                     $this->img_rb_x = $ximg + $w;
10106                 } elseif ($palign == 'C') {
10107                     $ximg = ($this->w - $x - $w) / 2;
10108                     // set right side coordinate
10109                     $this->img_rb_x = $ximg + $w;
10110                 } else {
10111                     $ximg = $this->w - $x - $w;
10112                     // set left side coordinate
10113                     $this->img_rb_x = $ximg;
10114                 }
10115             } else {
10116                 if ($palign == 'R') {
10117                     $ximg = $this->w - $this->rMargin - $w;
10118                     // set left side coordinate
10119                     $this->img_rb_x = $ximg;
10120                 } elseif ($palign == 'C') {
10121                     $ximg = ($this->w - $x - $w) / 2;
10122                     // set right side coordinate
10123                     $this->img_rb_x = $ximg + $w;
10124                 } else {
10125                     $ximg = $x;
10126                     // set right side coordinate
10127                     $this->img_rb_x = $ximg + $w;
10128                 }
10129             }
10130             if ($useBoundingBox) {
10131                 $dx = $ximg * $k - $x1;
10132                 $dy = $y * $k - $y1;
10133             } else {
10134                 $dx = $ximg * $k;
10135                 $dy = $y * $k;
10136             }
10137             // save the current graphic state
10138             $this->_out('q'.$this->epsmarker);
10139             // translate
10140             $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', 1, 0, 0, 1, $dx, $dy + ($this->hPt - (2 * $y * $k) - ($y2 - $y1))));
10141             // scale
10142             if (isset($scale_x)) {
10143                 $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $scale_x, 0, 0, $scale_y, $x1 * (1 - $scale_x), $y2 * (1 - $scale_y)));
10144             }
10145             // handle pc/unix/mac line endings
10146             preg_match('/[\r\n]+/s', $data, $regs);
10147             $lines = explode($regs[0], $data);
10148             $u=0;
10149             $cnt = count($lines);
10150             for ($i=0; $i < $cnt; ++$i) {
10151                 $line = $lines[$i];
10152                 if (($line == '') OR ($line{0} == '%')) {
10153                     continue;
10154                 }
10155                 $len = strlen($line);
10156                 $chunks = explode(' ', $line);
10157                 $cmd = array_pop($chunks);
10158                 // RGB
10159                 if (($cmd == 'Xa') OR ($cmd == 'XA')) {
10160                     $b = array_pop($chunks); 
10161                     $g = array_pop($chunks); 
10162                     $r = array_pop($chunks);
10163                     $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!
10164                     continue;
10165                 }
10166                 switch ($cmd) {
10167                     case 'm':
10168                     case 'l':
10169                     case 'v':
10170                     case 'y':
10171                     case 'c':
10172                     case 'k':
10173                     case 'K':
10174                     case 'g':
10175                     case 'G':
10176                     case 's':
10177                     case 'S':
10178                     case 'J':
10179                     case 'j':
10180                     case 'w':
10181                     case 'M':
10182                     case 'd':
10183                     case 'n':
10184                     case 'v': {
10185                         $this->_out($line);
10186                         break;
10187                     }
10188                     case 'x': {// custom fill color
10189                         list($c,$m,$y,$k) = $chunks;
10190                         $this->_out(''.$c.' '.$m.' '.$y.' '.$k.' k');
10191                         break;
10192                     }
10193                     case 'X': { // custom stroke color
10194                         list($c,$m,$y,$k) = $chunks;
10195                         $this->_out(''.$c.' '.$m.' '.$y.' '.$k.' K');
10196                         break;
10197                     }
10198                     case 'Y':
10199                     case 'N':
10200                     case 'V':
10201                     case 'L':
10202                     case 'C': {
10203                         $line{$len-1} = strtolower($cmd);
10204                         $this->_out($line);
10205                         break;
10206                     }
10207                     case 'b':
10208                     case 'B': {
10209                         $this->_out($cmd . '*');
10210                         break;
10211                     }
10212                     case 'f':
10213                     case 'F': {
10214                         if ($u > 0) {
10215                             $isU = false;
10216                             $max = min($i+5, $cnt);
10217                             for ($j=$i+1; $j < $max; ++$j)
10218                               $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U')));
10219                             if ($isU) {
10220                                 $this->_out('f*');
10221                             }
10222                         } else {
10223                             $this->_out('f*');
10224                         }
10225                         break;
10226                     }
10227                     case '*u': {
10228                         ++$u;
10229                         break;
10230                     }
10231                     case '*U': {
10232                         --$u;
10233                         break;
10234                     }
10235                 }
10236             }
10237             // restore previous graphic state
10238             $this->_out($this->epsmarker.'Q');
10239             if (!empty($border)) {
10240                 $bx = $x;
10241                 $by = $y;
10242                 $this->x = $x;
10243                 $this->y = $y;
10244                 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0);
10245                 $this->x = $bx;
10246                 $this->y = $by;
10247             }
10248             if ($link) {
10249                 $this->Link($ximg, $y, $w, $h, $link, 0);
10250             }
10251             // set pointer to align the successive text/objects
10252             switch($align) {
10253                 case 'T':{
10254                     $this->y = $y;
10255                     $this->x = $this->img_rb_x;
10256                     break;
10257                 }
10258                 case 'M':{
10259                     $this->y = $y + round($h/2);
10260                     $this->x = $this->img_rb_x;
10261                     break;
10262                 }
10263                 case 'B':{
10264                     $this->y = $this->img_rb_y;
10265                     $this->x = $this->img_rb_x;
10266                     break;
10267                 }
10268                 case 'N':{
10269                     $this->SetY($this->img_rb_y);
10270                     break;
10271                 }
10272                 default:{
10273                     break;
10274                 }
10275             }
10276             $this->endlinex = $this->img_rb_x;
10277         }
10278         
10284         public function setBarcode($bc='') {
10285             $this->barcode = $bc;
10286         }
10287         
10294         public function getBarcode() {
10295             return $this->barcode;
10296         }
10297         
10313         public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres=0.4, $style='', $align='') {
10314             if ($this->empty_string($code)) {
10315                 return;
10316             }
10317             require_once(dirname(__FILE__).'/barcodes.php');
10318             // save current graphic settings
10319             $gvars = $this->getGraphicVars();
10320             // create new barcode object
10321             $barcodeobj = new TCPDFBarcode($code, $type);
10322             $arrcode = $barcodeobj->getBarcodeArray();
10323             if ($arrcode === false) {
10324                 $this->Error('Error in 1D barcode string');
10325             }
10326             // set default values
10327             if (!isset($style['position'])) {
10328                 if ($this->rtl) {
10329                     $style['position'] = 'R';
10330                 } else {
10331                     $style['position'] = 'L';
10332                 }
10333             }
10334             if (!isset($style['padding'])) {
10335                 $style['padding'] = 0;
10336             }
10337             if (!isset($style['fgcolor'])) {
10338                 $style['fgcolor'] = array(0,0,0); // default black
10339             }
10340             if (!isset($style['bgcolor'])) {
10341                 $style['bgcolor'] = false; // default transparent
10342             }
10343             if (!isset($style['border'])) {
10344                 $style['border'] = false;
10345             }
10346             if (!isset($style['text'])) {
10347                 $style['text'] = false;
10348                 $fontsize = 0;
10349             }
10350             if ($style['text'] AND isset($style['font'])) {
10351                 if (isset($style['fontsize'])) {
10352                     $fontsize = $style['fontsize'];
10353                 } else {
10354                     $fontsize = 0;
10355                 }
10356                 $this->SetFont($style['font'], '', $fontsize);
10357             }
10358             if (!isset($style['stretchtext'])) {
10359                 $style['stretchtext'] = 4;
10360             }
10361             // set foreground color
10362             $this->SetDrawColorArray($style['fgcolor']);
10363             $this->SetTextColorArray($style['fgcolor']);
10364             if ($this->empty_string($w) OR ($w <= 0)) {
10365                 if ($this->rtl) {
10366                     $w = $this->x - $this->lMargin;
10367                 } else {
10368                     $w = $this->w - $this->rMargin - $this->x;
10369                 }
10370             }
10371             if ($this->empty_string($x)) {
10372                 $x = $this->GetX();
10373             }
10374             if ($this->rtl) {
10375                 $x = $this->w - $x;
10376             }
10377             if ($this->empty_string($y)) {
10378                 $y = $this->GetY();
10379             }
10380             if ($this->empty_string($xres)) {
10381                 $xres = 0.4;
10382             }
10383             $fbw = ($arrcode['maxw'] * $xres) + (2 * $style['padding']);
10384             $extraspace = ($this->cell_height_ratio * $fontsize / $this->k) + (2 * $style['padding']);
10385             if ($this->empty_string($h) OR ($h <= 0)) {
10386                 $h = 10 + $extraspace;
10387             }
10388             if ($this->checkPageBreak($h)) {
10389                 $y = $this->y;
10390             }
10391             // maximum bar heigth
10392             $barh = $h - $extraspace;
10393             switch ($style['position']) {
10394                 case 'L': { // left
10395                     if ($this->rtl) {
10396                         $xpos = $x - $w;
10397                     } else {
10398                         $xpos = $x;
10399                     }
10400                     break;
10401                 }
10402                 case 'C': { // center
10403                     $xdiff = (($w - $fbw) / 2);
10404                     if ($this->rtl) {
10405                         $xpos = $x - $w + $xdiff;
10406                     } else {
10407                         $xpos = $x + $xdiff;
10408                     }
10409                     break;
10410                 }
10411                 case 'R': { // right
10412                     if ($this->rtl) {
10413                         $xpos = $x - $fbw;
10414                     } else {
10415                         $xpos = $x + $w - $fbw;
10416                     }
10417                     break;
10418                 }
10419                 case 'S': { // stretch
10420                     $fbw = $w;
10421                     $xres = ($w - (2 * $style['padding'])) / $arrcode['maxw'];
10422                     if ($this->rtl) {
10423                         $xpos = $x - $w;
10424                     } else {
10425                         $xpos = $x;
10426                     }
10427                     break;
10428                 }
10429             }
10430             $xpos_rect = $xpos;
10431             $xpos = $xpos_rect + $style['padding'];
10432             $xpos_text = $xpos;
10433             // barcode is always printed in LTR direction
10434             $tempRTL = $this->rtl;
10435             $this->rtl = false;
10436             // print background color
10437             if ($style['bgcolor']) {
10438                 $this->Rect($xpos_rect, $y, $fbw, $h, 'DF', '', $style['bgcolor']);
10439             } elseif ($style['border']) {
10440                 $this->Rect($xpos_rect, $y, $fbw, $h, 'D');
10441             }
10442             // print bars
10443             if ($arrcode !== false) {
10444                 foreach ($arrcode['bcode'] as $k => $v) {
10445                     $bw = ($v['w'] * $xres);
10446                     if ($v['t']) {
10447                         // draw a vertical bar
10448                         $ypos = $y + $style['padding'] + ($v['p'] * $barh / $arrcode['maxh']);
10449                         $this->Rect($xpos, $ypos, $bw, ($v['h'] * $barh  / $arrcode['maxh']), 'F', array(), $style['fgcolor']);
10450                     }
10451                     $xpos += $bw;
10452                 }
10453             }
10454             // print text
10455             if ($style['text']) {
10456                 // print text
10457                 $this->x = $xpos_text;
10458                 $this->y = $y + $style['padding'] + $barh; 
10459                 $this->Cell(($arrcode['maxw'] * $xres), ($this->cell_height_ratio * $fontsize / $this->k), $code, 0, 0, 'C', 0, '', $style['stretchtext']);
10460             }
10461             // restore original direction
10462             $this->rtl = $tempRTL;
10463             // restore previous settings
10464             $this->setGraphicVars($gvars);
10465             // set bottomcoordinates
10466             $this->img_rb_y = $y + $h;
10467             if ($this->rtl) {
10468                 // set left side coordinate
10469                 $this->img_rb_x = ($this->w - $x - $w);
10470             } else {
10471                 // set right side coordinate
10472                 $this->img_rb_x = $x + $w;
10473             }
10474             // set pointer to align the successive text/objects
10475             switch($align) {
10476                 case 'T':{
10477                     $this->y = $y;
10478                     $this->x = $this->img_rb_x;
10479                     break;
10480                 }
10481                 case 'M':{
10482                     $this->y = $y + round($h/2);
10483                     $this->x = $this->img_rb_x;
10484                     break;
10485                 }
10486                 case 'B':{
10487                     $this->y = $this->img_rb_y;
10488                     $this->x = $this->img_rb_x;
10489                     break;
10490                 }
10491                 case 'N':{
10492                     $this->SetY($this->img_rb_y);
10493                     break;
10494                 }
10495                 default:{
10496                     break;
10497                 }
10498             }
10499         }
10500         
10516         public function writeBarcode($x, $y, $w, $h, $type, $style, $font, $xres, $code) {
10517             // convert old settings for the new write1DBarcode() function.
10518             $xres = 1 / $xres;
10519             $newstyle = array(
10520                 'position' => 'L',
10521                 'border' => false,
10522                 'padding' => 0,
10523                 'fgcolor' => array(0,0,0),
10524                 'bgcolor' => false,
10525                 'text' => true,
10526                 'font' => $font,
10527                 'fontsize' => 8,
10528                 'stretchtext' => 4
10529             );
10530             if ($style & 1) {
10531                 $newstyle['border'] = true;
10532             }
10533             if ($style & 2) {
10534                 $newstyle['bgcolor'] = false;
10535             }
10536             if ($style & 4) {
10537                 $newstyle['position'] = 'C';
10538             } elseif ($style & 8) {
10539                 $newstyle['position'] = 'L';
10540             } elseif ($style & 16) {
10541                 $newstyle['position'] = 'R';
10542             }
10543             if ($style & 128) {
10544                 $newstyle['text'] = true;
10545             }
10546             if ($style & 256) {
10547                 $newstyle['stretchtext'] = 4;
10548             }
10549             $this->write1DBarcode($code, $type, $x, $y, $w, $h, $xres, $newstyle, '');
10550         }
10551         
10566         public function write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='') {
10567             if ($this->empty_string($code)) {
10568                 return;
10569             }
10570             require_once(dirname(__FILE__).'/2dbarcodes.php');
10571             // save current graphic settings
10572             $gvars = $this->getGraphicVars();
10573             // create new barcode object
10574             $barcodeobj = new TCPDF2DBarcode($code, $type);
10575             $arrcode = $barcodeobj->getBarcodeArray();
10576             if ($arrcode === false) {
10577                 $this->Error('Error in 2D barcode string');
10578             }
10579             // set default values
10580             if (!isset($style['padding'])) {
10581                 $style['padding'] = 0;
10582             }
10583             if (!isset($style['fgcolor'])) {
10584                 $style['fgcolor'] = array(0,0,0); // default black
10585             }
10586             if (!isset($style['bgcolor'])) {
10587                 $style['bgcolor'] = false; // default transparent
10588             }
10589             if (!isset($style['border'])) {
10590                 $style['border'] = false;
10591             }
10592             // set foreground color
10593             $this->SetDrawColorArray($style['fgcolor']);
10594             if ($this->empty_string($x)) {
10595                 $x = $this->GetX();
10596             }
10597             if ($this->rtl) {
10598                 $x = $this->w - $x;
10599             }
10600             if ($this->empty_string($y)) {
10601                 $y = $this->GetY();
10602             }
10603             if ($this->empty_string($w) OR ($w <= 0)) {
10604                 if ($this->rtl) {
10605                     $w = $x - $this->lMargin;
10606                 } else {
10607                     $w = $this->w - $this->rMargin - $x;
10608                 }
10609             }
10610             if ($this->empty_string($h) OR ($h <= 0)) {
10611                 // 2d barcodes are square by default
10612                 $h = $w;
10613             }
10614             if ($this->checkPageBreak($h)) {
10615                 $y = $this->y;
10616             }
10617             // calculate barcode size (excluding padding)
10618             $bw = $w - (2 * $style['padding']);
10619             $bh = $h - (2 * $style['padding']);
10620             // calculate starting coordinates
10621             if ($this->rtl) {
10622                 $xpos = $x - $w;
10623             } else {
10624                 $xpos = $x;
10625             }
10626             $xpos += $style['padding'];
10627             $ypos = $y + $style['padding'];
10628             // barcode is always printed in LTR direction
10629             $tempRTL = $this->rtl;
10630             $this->rtl = false;
10631             // print background color
10632             if ($style['bgcolor']) {
10633                 $this->Rect($x, $y, $w, $h, 'DF', '', $style['bgcolor']);
10634             } elseif ($style['border']) {
10635                 $this->Rect($x, $y, $w, $h, 'D');
10636             }
10637             // print barcode cells
10638             if ($arrcode !== false) {
10639                 $rows = $arrcode['num_rows'];
10640                 $cols = $arrcode['num_cols'];
10641                 // calculate dimension of single barcode cell
10642                 $cw = $bw / $cols;
10643                 $ch = $bh / $rows;
10644                 // for each row
10645                 for ($r = 0; $r < $rows; ++$r) {
10646                     $xr = $xpos;
10647                     // for each column
10648                     for ($c = 0; $c < $cols; ++$c) {
10649                         if ($arrcode['bcode'][$r][$c] == 1) {
10650                             // draw a single barcode cell
10651                             $this->Rect($xr, $ypos, $cw, $ch, 'F', array(), $style['fgcolor']);
10652                         }
10653                         $xr += $cw;
10654                     }
10655                     $ypos += $ch;
10656                 }
10657             }
10658             // restore original direction
10659             $this->rtl = $tempRTL;
10660             // restore previous settings
10661             $this->setGraphicVars($gvars);
10662             // set bottomcoordinates
10663             $this->img_rb_y = $y + $h;
10664             if ($this->rtl) {
10665                 // set left side coordinate
10666                 $this->img_rb_x = ($this->w - $x - $w);
10667             } else {
10668                 // set right side coordinate
10669                 $this->img_rb_x = $x + $w;
10670             }
10671             // set pointer to align the successive text/objects
10672             switch($align) {
10673                 case 'T':{
10674                     $this->y = $y;
10675                     $this->x = $this->img_rb_x;
10676                     break;
10677                 }
10678                 case 'M':{
10679                     $this->y = $y + round($h/2);
10680                     $this->x = $this->img_rb_x;
10681                     break;
10682                 }
10683                 case 'B':{
10684                     $this->y = $this->img_rb_y;
10685                     $this->x = $this->img_rb_x;
10686                     break;
10687                 }
10688                 case 'N':{
10689                     $this->SetY($this->img_rb_y);
10690                     break;
10691                 }
10692                 default:{
10693                     break;
10694                 }
10695             }
10696         }
10697         
10713         public function getMargins() {
10714             $ret = array(
10715                 'left' => $this->lMargin,
10716                 'right' => $this->rMargin,
10717                 'top' => $this->tMargin,
10718                 'bottom' => $this->bMargin,
10719                 'header' => $this->header_margin,
10720                 'footer' => $this->footer_margin,
10721                 'cell' => $this->cMargin,
10722             );
10723             return $ret;
10724         }
10725         
10736         public function getOriginalMargins() {
10737             $ret = array(
10738                 'left' => $this->original_lMargin,
10739                 'right' => $this->original_rMargin
10740             );
10741             return $ret;
10742         }
10743         
10750         public function getFontSize() {
10751             return $this->FontSize;
10752         }
10753         
10760         public function getFontSizePt() {
10761             return $this->FontSizePt;
10762         }
10763 
10770         public function getFontFamily() {
10771             return $this->FontFamily;
10772         }
10773 
10780         public function getFontStyle() {
10781             return $this->FontStyle;
10782         }
10783         
10804         public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=0, $reseth=true, $align='', $autopadding=true) {
10805             return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true, $autopadding, 0);
10806         }
10807         
10816         protected function getHtmlDomArray($html) {
10817             // remove all unsupported tags (the line below lists all supported tags)
10818             $html = strip_tags($html, '<marker/><a><b><blockquote><br><br/><dd><del><div><dl><dt><em><font><h1><h2><h3><h4><h5><h6><hr><i><img><li><ol><p><pre><small><span><strong><sub><sup><table><tcpdf><td><th><thead><tr><tt><u><ul>');
10819             //replace some blank characters
10820             $html = preg_replace('/<pre/', '<xre', $html); // preserve pre tag
10821             $html = preg_replace('/<(table|tr|td|th|blockquote|dd|div|dt|h1|h2|h3|h4|h5|h6|br|hr|li|ol|ul|p)([^>]*)>[\n\r\t]+/', '<\\1\\2>', $html);
10822             $html = preg_replace('@(\r\n|\r)@', "\n", $html);
10823             $repTable = array("\t" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\");
10824             $html = strtr($html, $repTable);
10825             while (preg_match("'<xre([^>]*)>(.*?)\n(.*?)</pre>'si", $html)) {
10826                 // preserve newlines on <pre> tag
10827                 $html = preg_replace("'<xre([^>]*)>(.*?)\n(.*?)</pre>'si", "<xre\\1>\\2<br />\\3</pre>", $html);
10828             }
10829             $html = str_replace("\n", ' ', $html);
10830             // remove extra spaces from code
10831             $html = preg_replace('/[\s]+<\/(table|tr|td|th|ul|ol|li)>/', '</\\1>', $html);
10832             $html = preg_replace('/[\s]+<(tr|td|th|ul|ol|li|br)/', '<\\1', $html);
10833             $html = preg_replace('/<\/(table|tr|td|th|blockquote|dd|div|dt|h1|h2|h3|h4|h5|h6|hr|li|ol|ul|p)>[\s]+</', '</\\1><', $html);
10834             $html = preg_replace('/<\/(td|th)>/', '<marker style="font-size:0"/></\\1>', $html);
10835             $html = preg_replace('/<\/table>([\s]*)<marker style="font-size:0"\/>/', '</table>', $html);
10836             $html = preg_replace('/<img/', ' <img', $html);
10837             $html = preg_replace('/<img([^>]*)>/xi', '<img\\1><span></span>', $html);
10838             $html = preg_replace('/<xre/', '<pre', $html); // restore pre tag
10839             // trim string
10840             $html = preg_replace('/^[\s]+/', '', $html);
10841             $html = preg_replace('/[\s]+$/', '', $html);
10842             // pattern for generic tag
10843             $tagpattern = '/(<[^>]+>)/';
10844             // explodes the string
10845             $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
10846             // count elements
10847             $maxel = count($a);
10848             $elkey = 0;
10849             $key = 0;
10850             // create an array of elements
10851             $dom = array();
10852             $dom[$key] = array();
10853             // set first void element
10854             $dom[$key]['tag'] = false;
10855             $dom[$key]['value'] = '';
10856             $dom[$key]['parent'] = 0;
10857             $dom[$key]['fontname'] = $this->FontFamily;
10858             $dom[$key]['fontstyle'] = $this->FontStyle;
10859             $dom[$key]['fontsize'] = $this->FontSizePt;
10860             $dom[$key]['bgcolor'] = false;
10861             $dom[$key]['fgcolor'] = $this->fgcolor;
10862             $dom[$key]['align'] = '';
10863             $dom[$key]['listtype'] = '';
10864             $thead = false; // true when we are inside the THEAD tag
10865             ++$key;
10866             $level = array();
10867             array_push($level, 0); // root
10868             while ($elkey < $maxel) {
10869                 $dom[$key] = array();
10870                 $element = $a[$elkey];
10871                 $dom[$key]['elkey'] = $elkey;
10872                 if (preg_match($tagpattern, $element)) {
10873                     // html tag
10874                     $element = substr($element, 1, -1);
10875                     // get tag name
10876                     preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag);
10877                     $tagname = strtolower($tag[1]);
10878                     // check if we are inside a table header
10879                     if ($tagname == 'thead') {
10880                         if ($element{0} == '/') {
10881                             $thead = false;
10882                         } else {
10883                             $thead = true;
10884                         }
10885                         ++$elkey;
10886                         continue;
10887                     }
10888                     $dom[$key]['tag'] = true;
10889                     $dom[$key]['value'] = $tagname;
10890                     if ($element{0} == '/') {
10891                         // closing html tag
10892                         $dom[$key]['opening'] = false;
10893                         $dom[$key]['parent'] = end($level);
10894                         array_pop($level);
10895                         $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname'];
10896                         $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle'];
10897                         $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize'];
10898                         $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor'];
10899                         $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor'];
10900                         $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align'];
10901                         if (isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'])) {
10902                             $dom[$key]['listtype'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'];
10903                         }
10904                         // set the number of columns in table tag
10905                         if (($dom[$key]['value'] == 'tr') AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) {
10906                             $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols'];
10907                         }
10908                         if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
10909                             $dom[($dom[$key]['parent'])]['content'] = '';
10910                             for ($i = ($dom[$key]['parent'] + 1); $i < $key; ++$i) {
10911                                 $dom[($dom[$key]['parent'])]['content'] .= $a[$dom[$i]['elkey']];
10912                             }
10913                             $key = $i;
10914                         }
10915                         // store header rows on a new table
10916                         if (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['thead'] == true)) {
10917                             if ($this->empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) {
10918                                 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']];
10919                             }
10920                             for ($i = $dom[$key]['parent']; $i <= $key; ++$i) {
10921                                 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']];
10922                             }
10923                         }
10924                         if (($dom[$key]['value'] == 'table') AND (!$this->empty_string($dom[($dom[$key]['parent'])]['thead']))) {
10925                             $dom[($dom[$key]['parent'])]['thead'] .= '</table>';
10926                         }
10927                     } else {
10928                         // opening html tag
10929                         $dom[$key]['opening'] = true;
10930                         $dom[$key]['parent'] = end($level);
10931                         if (substr($element, -1, 1) != '/') {
10932                             // not self-closing tag
10933                             array_push($level, $key);
10934                             $dom[$key]['self'] = false;
10935                         } else {
10936                             $dom[$key]['self'] = true;
10937                         }
10938                         // copy some values from parent
10939                         $parentkey = 0;
10940                         if ($key > 0) {
10941                             $parentkey = $dom[$key]['parent'];
10942                             $dom[$key]['fontname'] = $dom[$parentkey]['fontname'];
10943                             $dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle'];
10944                             $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'];
10945                             $dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor'];
10946                             $dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor'];
10947                             $dom[$key]['align'] = $dom[$parentkey]['align'];
10948                             $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
10949                         }
10950                         // get attributes
10951                         preg_match_all('/([^=\s]*)=["]?([^"]*)["]?/', $element, $attr_array, PREG_PATTERN_ORDER);
10952                         $dom[$key]['attribute'] = array(); // reset attribute array
10953                         while (list($id, $name) = each($attr_array[1])) {
10954                             $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id];
10955                         }
10956                         // split style attributes
10957                         if (isset($dom[$key]['attribute']['style'])) {
10958                             // get style attributes
10959                             preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
10960                             $dom[$key]['style'] = array(); // reset style attribute array
10961                             while (list($id, $name) = each($style_array[1])) {
10962                                 $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]);
10963                             }
10964                             // --- get some style attributes ---
10965                             if (isset($dom[$key]['style']['font-family'])) {
10966                                 // font family
10967                                 if (isset($dom[$key]['style']['font-family'])) {
10968                                     $fontslist = split(',', strtolower($dom[$key]['style']['font-family']));
10969                                     foreach ($fontslist as $font) {
10970                                         $font = trim(strtolower($font));
10971                                         if (in_array($font, $this->fontlist) OR in_array($font, $this->fontkeys)) {
10972                                             $dom[$key]['fontname'] = $font;
10973                                             break;
10974                                         }
10975                                     }
10976                                 }
10977                             }
10978                             // list-style-type
10979                             if (isset($dom[$key]['style']['list-style-type'])) {
10980                                 $dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type']));
10981                                 if ($dom[$key]['listtype'] == 'inherit') {
10982                                     $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
10983                                 }
10984                             }
10985                             // font size
10986                             if (isset($dom[$key]['style']['font-size'])) {
10987                                 $fsize = trim($dom[$key]['style']['font-size']);
10988                                 switch ($fsize) {
10989                                     // absolute-size
10990                                     case 'xx-small': {
10991                                         $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 4;
10992                                         break;
10993                                     }
10994                                     case 'x-small': {
10995                                         $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 3;
10996                                         break;
10997                                     }
10998                                     case 'small': {
10999                                         $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 2;
11000                                         break;
11001                                     }
11002                                     case 'medium': {
11003                                         $dom[$key]['fontsize'] = $dom[0]['fontsize'];
11004                                         break;
11005                                     }
11006                                     case 'large': {
11007                                         $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 2;
11008                                         break;
11009                                     }
11010                                     case 'x-large': {
11011                                         $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 4;
11012                                         break;
11013                                     }
11014                                     case 'xx-large': {
11015                                         $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 6;
11016                                         break;
11017                                     }
11018                                     // relative-size
11019                                     case 'smaller': {
11020                                         $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] - 3;
11021                                         break;
11022                                     }
11023                                     case 'larger': {
11024                                         $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] + 3;
11025                                         break;
11026                                     }
11027                                     default: {
11028                                         $dom[$key]['fontsize'] = $this->getHTMLUnitToUnits($fsize, $dom[$parentkey]['fontsize'], 'pt', true);
11029                                     }
11030                                 }
11031                             }
11032                             // font style
11033                             if (isset($dom[$key]['style']['font-weight']) AND (strtolower($dom[$key]['style']['font-weight']{0}) == 'b')) {
11034                                 $dom[$key]['fontstyle'] .= 'B';
11035                             }
11036                             if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style']{0}) == 'i')) {
11037                                 $dom[$key]['fontstyle'] .= '"I';
11038                             }
11039                             // font color
11040                             if (isset($dom[$key]['style']['color']) AND (!$this->empty_string($dom[$key]['style']['color']))) {
11041                                 $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['color']);
11042                             }
11043                             // background color
11044                             if (isset($dom[$key]['style']['background-color']) AND (!$this->empty_string($dom[$key]['style']['background-color']))) {
11045                                 $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['background-color']);
11046                             }
11047                             // text-decoration
11048                             if (isset($dom[$key]['style']['text-decoration'])) {
11049                                 $decors = explode(' ', strtolower($dom[$key]['style']['text-decoration']));
11050                                 foreach ($decors as $dec) {
11051                                     $dec = trim($dec);
11052                                     if (!$this->empty_string($dec)) {
11053                                         if ($dec{0} == 'u') {
11054                                             $dom[$key]['fontstyle'] .= 'U';
11055                                         } elseif ($dec{0} == 'l') {
11056                                             $dom[$key]['fontstyle'] .= 'D';
11057                                         }
11058                                     }
11059                                 }
11060                             }
11061                             // check for width attribute
11062                             if (isset($dom[$key]['style']['width'])) {
11063                                 $dom[$key]['width'] = $dom[$key]['style']['width'];
11064                             }
11065                             // check for height attribute
11066                             if (isset($dom[$key]['style']['height'])) {
11067                                 $dom[$key]['height'] = $dom[$key]['style']['height'];
11068                             }
11069                             // check for text alignment
11070                             if (isset($dom[$key]['style']['text-align'])) {
11071                                 $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align']{0});
11072                             }
11073                             // check for border attribute
11074                             if (isset($dom[$key]['style']['border'])) {
11075                                 $dom[$key]['attribute']['border'] = $dom[$key]['style']['border'];
11076                             }
11077                         }
11078                         // check for font tag
11079                         if ($dom[$key]['value'] == 'font') {
11080                             // font family
11081                             if (isset($dom[$key]['attribute']['face'])) {
11082                                 $fontslist = split(',', strtolower($dom[$key]['attribute']['face']));
11083                                 foreach ($fontslist as $font) {
11084                                     $font = trim(strtolower($font));
11085                                     if (in_array($font, $this->fontlist) OR in_array($font, $this->fontkeys)) {
11086                                         $dom[$key]['fontname'] = $font;
11087                                         break;
11088                                     }
11089                                 }
11090                             }
11091                             // font size
11092                             if (isset($dom[$key]['attribute']['size'])) {
11093                                 if ($key > 0) {
11094                                     if ($dom[$key]['attribute']['size']{0} == '+') {
11095                                         $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1));
11096                                     } elseif ($dom[$key]['attribute']['size']{0} == '-') {
11097                                         $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1));
11098                                     } else {
11099                                         $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
11100                                     }
11101                                 } else {
11102                                     $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
11103                                 }
11104                             }
11105                         }
11106                         // force natural alignment for lists
11107                         if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl'))
11108                             AND (!isset($dom[$key]['align']) OR $this->empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) {
11109                             if ($this->rtl) {
11110                                 $dom[$key]['align'] = 'R';
11111                             } else {
11112                                 $dom[$key]['align'] = 'L';
11113                             }
11114                         }
11115                         if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) {
11116                             $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO;
11117                         }
11118                         if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) {
11119                             $dom[$key]['fontstyle'] .= 'B';
11120                         }
11121                         if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) {
11122                             $dom[$key]['fontstyle'] .= 'I';
11123                         }
11124                         if ($dom[$key]['value'] == 'u') {
11125                             $dom[$key]['fontstyle'] .= 'U';
11126                         }
11127                         if ($dom[$key]['value'] == 'del') {
11128                             $dom[$key]['fontstyle'] .= 'D';
11129                         }
11130                         if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) {
11131                             $dom[$key]['fontname'] = $this->default_monospaced_font;
11132                         }
11133                         if (($dom[$key]['value']{0} == 'h') AND (intval($dom[$key]['value']{1}) > 0) AND (intval($dom[$key]['value']{1}) < 7)) {
11134                             $headsize = (4 - intval($dom[$key]['value']{1})) * 2;
11135                             $dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize;
11136                             $dom[$key]['fontstyle'] .= 'B';
11137                         }
11138                         if (($dom[$key]['value'] == 'table')) {
11139                             $dom[$key]['rows'] = 0; // number of rows
11140                             $dom[$key]['trids'] = array(); // IDs of TR elements
11141                             $dom[$key]['thead'] = ''; // table header rows
11142                         }
11143                         if (($dom[$key]['value'] == 'tr')) {
11144                             $dom[$key]['cols'] = 0;
11145                             // store the number of rows on table element
11146                             ++$dom[($dom[$key]['parent'])]['rows'];
11147                             // store the TR elements IDs on table element
11148                             array_push($dom[($dom[$key]['parent'])]['trids'], $key);
11149                             if ($thead) {
11150                                 $dom[$key]['thead'] = true;
11151                             } else {
11152                                 $dom[$key]['thead'] = false;
11153                             }
11154                         }
11155                         if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) {
11156                             if (isset($dom[$key]['attribute']['colspan'])) {
11157                                 $colspan = intval($dom[$key]['attribute']['colspan']);
11158                             } else {
11159                                 $colspan = 1;
11160                             }
11161                             $dom[$key]['attribute']['colspan'] = $colspan;
11162                             $dom[($dom[$key]['parent'])]['cols'] += $colspan;
11163                         }
11164                         // set foreground color attribute
11165                         if (isset($dom[$key]['attribute']['color']) AND (!$this->empty_string($dom[$key]['attribute']['color']))) {
11166                             $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['color']);
11167                         }
11168                         // set background color attribute
11169                         if (isset($dom[$key]['attribute']['bgcolor']) AND (!$this->empty_string($dom[$key]['attribute']['bgcolor']))) {
11170                             $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['bgcolor']);
11171                         }
11172                         // check for width attribute
11173                         if (isset($dom[$key]['attribute']['width'])) {
11174                             $dom[$key]['width'] = $dom[$key]['attribute']['width'];
11175                         }
11176                         // check for height attribute
11177                         if (isset($dom[$key]['attribute']['height'])) {
11178                             $dom[$key]['height'] = $dom[$key]['attribute']['height'];
11179                         }
11180                         // check for text alignment
11181                         if (isset($dom[$key]['attribute']['align']) AND (!$this->empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) {
11182                             $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align']{0});
11183                         }
11184                     } // end opening tag
11185                 } else {
11186                     // text
11187                     $dom[$key]['tag'] = false;
11188                     $dom[$key]['value'] = stripslashes($this->unhtmlentities($element));
11189                     $dom[$key]['parent'] = end($level);
11190                 }
11191                 ++$elkey;
11192                 ++$key;
11193             }
11194             return $dom;
11195         }
11196         
11209         public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') {
11210             $gvars = $this->getGraphicVars();
11211             // store current values
11212             $prevPage = $this->page;
11213             $prevlMargin = $this->lMargin;
11214             $prevrMargin = $this->rMargin;
11215             $curfontname = $this->FontFamily;
11216             $curfontstyle = $this->FontStyle;
11217             $curfontsize = $this->FontSizePt;   
11218             $this->newline = true;
11219             $minstartliney = $this->y;
11220             $yshift = 0;
11221             $startlinepage = $this->page;
11222             $newline = true;
11223             $loop = 0;
11224             $curpos = 0;
11225             $blocktags = array('blockquote','br','dd','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','ul');
11226             $this->premode = false;
11227             if (isset($this->PageAnnots[$this->page])) {
11228                 $pask = count($this->PageAnnots[$this->page]);
11229             } else {
11230                 $pask = 0;
11231             }
11232             if (isset($this->footerlen[$this->page])) {
11233                 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
11234             } else {
11235                 $this->footerpos[$this->page] = $this->pagelen[$this->page];
11236             }
11237             $startlinepos = $this->footerpos[$this->page];
11238             $lalign = $align;
11239             $plalign = $align;
11240             if ($this->rtl) {
11241                 $w = $this->x - $this->lMargin;
11242             } else {
11243                 $w = $this->w - $this->rMargin - $this->x;
11244             }
11245             $w -= (2 * $this->cMargin);
11246             if ($cell) {
11247                 if ($this->rtl) {
11248                     $this->x -= $this->cMargin;
11249                 } else {
11250                     $this->x += $this->cMargin;
11251                 }
11252             }
11253             if ($this->customlistindent >= 0) {
11254                 $this->listindent = $this->customlistindent;
11255             } else {
11256                 $this->listindent = $this->GetStringWidth('0000');
11257             }
11258             $this->listnum = 0;
11259             if (($this->empty_string($this->lasth)) OR ($reseth)) {
11260                 //set row height
11261                 $this->lasth = $this->FontSize * $this->cell_height_ratio; 
11262             }
11263             $dom = $this->getHtmlDomArray($html);
11264             $maxel = count($dom);
11265             $key = 0;
11266             while ($key < $maxel) {
11267                 if ($dom[$key]['tag'] OR ($key == 0)) {
11268                     if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) {
11269                         $dom[$key]['align'] = ($this->rtl) ? 'R' : 'L';
11270                     }
11271                     // vertically align image in line
11272                     if ((!$this->newline)
11273                         AND ($dom[$key]['value'] == 'img')
11274                         AND (isset($dom[$key]['attribute']['height']))
11275                         AND ($dom[$key]['attribute']['height'] > 0)
11276                         AND (!((($this->y + $this->getHTMLUnitToUnits($dom[$key]['attribute']['height'], $this->lasth, 'px')) > $this->PageBreakTrigger)
11277                             AND (!$this->InFooter)
11278                             AND $this->AcceptPageBreak()))
11279                         ) {
11280                         if ($this->page > $startlinepage) {
11281                             // fix lines splitted over two pages
11282                             if (isset($this->footerlen[$startlinepage])) {
11283                                 $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
11284                             }
11285                             // line to be moved one page forward
11286                             $pagebuff = $this->getPageBuffer($startlinepage);
11287                             $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
11288                             $tstart = substr($pagebuff, 0, $startlinepos);
11289                             $tend = substr($this->getPageBuffer($startlinepage), $curpos);
11290                             // remove line start from previous page
11291                             $this->setPageBuffer($startlinepage, $tstart.''.$tend);
11292                             $pagebuff = $this->getPageBuffer($this->page);
11293                             $tstart = substr($pagebuff, 0, $this->intmrk[$this->page]);
11294                             $tend = substr($pagebuff, $this->intmrk[$this->page]);
11295                             // add line start to current page
11296                             $yshift = $minstartliney - $this->y;
11297                             $try = sprintf('1 0 0 1 0 %.3F cm', ($yshift * $this->k));
11298                             $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
11299                             // shift the annotations and links
11300                             if (isset($this->PageAnnots[$startlinepage])) {
11301                                 foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
11302                                     if ($pak >= $pask) {
11303                                         $this->PageAnnots[$this->page][] = $pac;
11304                                         unset($this->PageAnnots[$startlinepage][$pak]);
11305                                         $npak = count($this->PageAnnots[$this->page]) - 1;
11306                                         $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
11307                                     }
11308                                 }
11309                             }
11310                         }
11311                         $this->y += (($curfontsize / $this->k) - $this->getHTMLUnitToUnits($dom[$key]['attribute']['height'], $this->lasth, 'px'));
11312                         $minstartliney = min($this->y, $minstartliney);
11313                     } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize'])) {
11314                         // account for different font size
11315                         $pfontname = $curfontname;
11316                         $pfontstyle = $curfontstyle;
11317                         $pfontsize = $curfontsize;
11318                         $fontname = isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname;
11319                         $fontstyle = isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle;
11320                         $fontsize = isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize;
11321                         if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)) {
11322                             $this->SetFont($fontname, $fontstyle, $fontsize);
11323                             $this->lasth = $this->FontSize * $this->cell_height_ratio;
11324                             if (is_numeric($fontsize) AND ($fontsize > 0)
11325                                 AND is_numeric($curfontsize) AND ($curfontsize > 0)
11326                                 AND ($fontsize != $curfontsize) AND (!$this->newline)
11327                                 AND ($key < ($maxel - 1))
11328                                 ) {
11329                                 if ((!$this->newline) AND ($this->page > $startlinepage)) {
11330                                     // fix lines splitted over two pages
11331                                     if (isset($this->footerlen[$startlinepage])) {
11332                                         $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
11333                                     }
11334                                     // line to be moved one page forward
11335                                     $pagebuff = $this->getPageBuffer($startlinepage);
11336                                     $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
11337                                     $tstart = substr($pagebuff, 0, $startlinepos);
11338                                     $tend = substr($this->getPageBuffer($startlinepage), $curpos);
11339                                     // remove line start from previous page
11340                                     $this->setPageBuffer($startlinepage, $tstart.''.$tend);
11341                                     $pagebuff = $this->getPageBuffer($this->page);
11342                                     $tstart = substr($pagebuff, 0, $this->intmrk[$this->page]);
11343                                     $tend = substr($pagebuff, $this->intmrk[$this->page]);
11344                                     // add line start to current page
11345                                     $yshift = $minstartliney - $this->y;
11346                                     $try = sprintf('1 0 0 1 0 %.3F cm', ($yshift * $this->k));
11347                                     $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
11348                                     // shift the annotations and links
11349                                     if (isset($this->PageAnnots[$startlinepage])) {
11350                                         foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
11351                                             if ($pak >= $pask) {
11352                                                 $this->PageAnnots[$this->page][] = $pac;
11353                                                 unset($this->PageAnnots[$startlinepage][$pak]);
11354                                                 $npak = count($this->PageAnnots[$this->page]) - 1;
11355                                                 $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
11356                                             }
11357                                         }
11358                                     }
11359                                 }
11360                                 $this->y += (($curfontsize - $fontsize) / $this->k);
11361                                 $minstartliney = min($this->y, $minstartliney);
11362                             }
11363                             $curfontname = $fontname;
11364                             $curfontstyle = $fontstyle;
11365                             $curfontsize = $fontsize;
11366                         }
11367                     }
11368                     if (($plalign == 'J') AND (in_array($dom[$key]['value'], $blocktags))) {
11369                         $plalign = '';
11370                     }
11371                     // get current position on page buffer
11372                     $curpos = $this->pagelen[$startlinepage];
11373                     if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) {
11374                         $this->SetFillColorArray($dom[$key]['bgcolor']);
11375                         $wfill = true;
11376                     } else {
11377                         $wfill = $fill | false;
11378                     }
11379                     if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) {
11380                         $this->SetTextColorArray($dom[$key]['fgcolor']);
11381                     }
11382                     if (isset($dom[$key]['align'])) {
11383                         $lalign = $dom[$key]['align'];
11384                     }
11385                     if ($this->empty_string($lalign)) {
11386                         $lalign = $align;
11387                     }
11388                 }
11389                 // align lines
11390                 if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) {
11391                     $newline = true;
11392                     // we are at the beginning of a new line
11393                     if (isset($startlinex)) {
11394                         $yshift = $minstartliney - $startliney;
11395                         if (($yshift > 0) OR ($this->page > $startlinepage)) {
11396                             $yshift = 0;
11397                         }
11398                         if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl))))) OR ($yshift < 0)) {
11399                             // the last line must be shifted to be aligned as requested
11400                             $linew = abs($this->endlinex - $startlinex);
11401                             $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
11402                             if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
11403                                 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
11404                                 $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
11405                             } elseif (isset($opentagpos)) {
11406                                 $midpos = $opentagpos;
11407                             } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
11408                                 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
11409                                 $midpos = $this->footerpos[$startlinepage];
11410                             } else {
11411                                 $midpos = 0;
11412                             }
11413                             if ($midpos > 0) {
11414                                 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
11415                                 $pend = substr($this->getPageBuffer($startlinepage), $midpos);
11416                             } else {
11417                                 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
11418                                 $pend = '';
11419                             }
11420                             // calculate shifting amount
11421                             $tw = $w;
11422                             if ($this->lMargin != $prevlMargin) {
11423                                 $tw += ($prevlMargin - $this->lMargin);
11424                             }
11425                             if ($this->rMargin != $prevrMargin) {
11426                                 $tw += ($prevrMargin - $this->rMargin);
11427                             }
11428                             $mdiff = abs($tw - $linew);
11429                             $t_x = 0;
11430                             if ($plalign == 'C') {
11431                                 if ($this->rtl) {
11432                                     $t_x = -($mdiff / 2);
11433                                 } else {
11434                                     $t_x = ($mdiff / 2);
11435                                 }
11436                             } elseif (($plalign == 'R') AND (!$this->rtl)) {
11437                                 // right alignment on LTR document
11438                                 $t_x = $mdiff;  
11439                             } elseif (($plalign == 'L') AND ($this->rtl)) {
11440                                 // left alignment on RTL document
11441                                 $t_x = -$mdiff;
11442                             } elseif (($plalign == 'J') AND ($plalign == $lalign)) {
11443                                 // Justification
11444                                 if ($this->rtl OR $this->tmprtl) {
11445                                     $t_x = $this->lMargin - $this->endlinex;
11446                                 }
11447                                 $no = 0;
11448                                 $ns = 0;
11449                                 $pmidtemp = $pmid;
11450                                 // escape special characters
11451                                 $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
11452                                 $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
11453                                 // search spaces
11454                                 if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER)) {
11455                                     $maxkk = count($lnstring[1]) - 1;
11456                                     for ($kk=0; $kk <= $maxkk; ++$kk) {
11457                                         // restore special characters
11458                                         $lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]);
11459                                         $lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]);
11460                                         if ($kk == $maxkk) {
11461                                             if ($this->rtl OR $this->tmprtl) {
11462                                                 $tvalue = ltrim($lnstring[1][$kk]);
11463                                             } else {
11464                                                 $tvalue = rtrim($lnstring[1][$kk]);
11465                                             }
11466                                         } else {
11467                                             $tvalue = $lnstring[1][$kk];
11468                                         }
11469                                         // count spaces on line
11470                                         $no += substr_count($lnstring[1][$kk], chr(32));
11471                                         $ns += substr_count($tvalue, chr(32));
11472                                     }
11473                                     if ($this->rtl OR $this->tmprtl) {
11474                                         $t_x = $this->lMargin - $this->endlinex - (($no - $ns - 1) * $this->GetStringWidth(chr(32)));
11475                                     }
11476                                     // calculate additional space to add to each space
11477                                     $spacewidth = (($tw - $linew + (($no - $ns) * $this->GetStringWidth(chr(32)))) / ($ns?$ns:1)) * $this->k;
11478                                     $spacewidthu = ($tw - $linew + ($no * $this->GetStringWidth(chr(32)))) / ($ns?$ns:1) / $this->FontSize / $this->k;
11479                                     $nsmax = $ns;
11480                                     $ns = 0;
11481                                     reset($lnstring);
11482                                     $offset = 0;
11483                                     $strcount = 0;
11484                                     $prev_epsposbeg = 0;
11485                                     global $spacew;
11486                                     while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE, $offset) == 1) {
11487                                         if ($this->rtl OR $this->tmprtl) {
11488                                             $spacew = ($spacewidth * ($nsmax - $ns));
11489                                         } else {
11490                                             $spacew = ($spacewidth * $ns);
11491                                         }
11492                                         $offset = $strpiece[2][1] + strlen($strpiece[2][0]);
11493                                         $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, $offset);
11494                                         $epsposend = strpos($pmid, $this->epsmarker.'Q', $offset) + strlen($this->epsmarker.'Q');
11495                                         if ((($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend))
11496                                             OR (($epsposbeg === false) AND ($epsposend > 0) AND ($offset < $epsposend))) {
11497                                             // shift EPS images
11498                                             $trx = sprintf('1 0 0 1 %.3F 0 cm', $spacew);
11499                                             $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, ($prev_epsposbeg - 6));
11500                                             $pmid_b = substr($pmid, 0, $epsposbeg);
11501                                             $pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg));
11502                                             $pmid_e = substr($pmid, $epsposend);
11503                                             $pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e;
11504                                             $offset = $epsposend;
11505                                             continue;
11506                                         }
11507                                         $prev_epsposbeg = $epsposbeg;
11508                                         $currentxpos = 0;
11509                                         // shift blocks of code
11510                                         switch ($strpiece[2][0]) {
11511                                             case 'Td':
11512                                             case 'cm':
11513                                             case 'm':
11514                                             case 'l': {
11515                                                 // get current X position
11516                                                 preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
11517                                                 $currentxpos = $xmatches[1];
11518                                                 if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) {
11519                                                     if ($strcount == $maxkk) {
11520                                                         if ($this->rtl OR $this->tmprtl) {
11521                                                             $tvalue = $lnstring[1][$strcount];
11522                                                         } else {
11523                                                             $tvalue = rtrim($lnstring[1][$strcount]);
11524                                                         }
11525                                                     } else {
11526                                                         $tvalue = $lnstring[1][$strcount];
11527                                                     }
11528                                                     $ns += substr_count($tvalue, chr(32));
11529                                                     ++$strcount;
11530                                                 }
11531                                                 if ($this->rtl OR $this->tmprtl) {
11532                                                     $spacew = ($spacewidth * ($nsmax - $ns));
11533                                                 }
11534                                                 // justify block
11535                                                 $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x',
11536                                                     create_function('$matches', 'global $spacew;
11537                                                     $newx = sprintf("%.2F",(floatval($matches[1]) + $spacew));
11538                                                     return "".$newx." ".$matches[2]." x*#!#*x".$matches[3].$matches[4];'), $pmid, 1);
11539                                                 break;
11540                                             }
11541                                             case 're': {
11542                                                 // get current X position
11543                                                 preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
11544                                                 $currentxpos = $xmatches[1];
11545                                                 // justify block
11546                                                 $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x',
11547                                                     create_function('$matches', 'global $spacew;
11548                                                     $newx = sprintf("%.2F",(floatval($matches[1]) + $spacew));
11549                                                     return "".$newx." ".$matches[2]." ".$matches[3]." ".$matches[4]." x*#!#*x".$matches[5].$matches[6];'), $pmid, 1);
11550                                                 break;
11551                                             }
11552                                             case 'c': {
11553                                                 // get current X position
11554                                                 preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
11555                                                 $currentxpos = $xmatches[1];
11556                                                 // justify block
11557                                                 $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x',
11558                                                     create_function('$matches', 'global $spacew;
11559                                                     $newx1 = sprintf("%.3F",(floatval($matches[1]) + $spacew));
11560                                                     $newx2 = sprintf("%.3F",(floatval($matches[3]) + $spacew));
11561                                                     $newx3 = sprintf("%.3F",(floatval($matches[5]) + $spacew));
11562                                                     return "".$newx1." ".$matches[2]." ".$newx2." ".$matches[4]." ".$newx3." ".$matches[6]." x*#!#*x".$matches[7].$matches[8];'), $pmid, 1);
11563                                                 break;
11564                                             }
11565                                         }
11566                                         // shift the annotations and links
11567                                         if (isset($this->PageAnnots[$this->page])) {
11568                                             foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
11569                                                 if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
11570                                                     $this->PageAnnots[$this->page][$pak]['x'] += ($spacew / $this->k);
11571                                                     $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
11572                                                     break;
11573                                                 }
11574                                             }
11575                                         }
11576                                     } // end of while
11577                                     // remove markers
11578                                     $pmid = str_replace('x*#!#*x', '', $pmid);
11579                                     if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
11580                                         // multibyte characters
11581                                         $spacew = $spacewidthu;
11582                                         $pmidtemp = $pmid;
11583                                         // escape special characters
11584                                         $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
11585                                         $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
11586                                         $pmid = preg_replace_callback("/\[\(([^\)]*)\)\]/x",
11587                                                     create_function('$matches', 'global $spacew;
11588                                                     $matches[1] = str_replace("#!#OP#!#", "(", $matches[1]);
11589                                                     $matches[1] = str_replace("#!#CP#!#", ")", $matches[1]);
11590                                                     return "[(".str_replace(chr(0).chr(32), ") ".(-2830 * $spacew)." (", $matches[1]).")]";'), $pmidtemp);
11591                                         $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend);
11592                                         $endlinepos = strlen($pstart."\n".$pmid."\n");
11593                                     } else {
11594                                         // non-unicode (single-byte characters)
11595                                         $rs = sprintf("%.3F Tw", $spacewidth);
11596                                         $pmid = preg_replace("/\[\(/x", $rs.' [(', $pmid);
11597                                         $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend);
11598                                         $endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n");
11599                                     }
11600                                 }
11601                             } // end of J
11602                             if (($t_x != 0) OR ($yshift < 0)) {
11603                                 // shift the line
11604                                 $trx = sprintf('1 0 0 1 %.3F %.3F cm', ($t_x * $this->k), ($yshift * $this->k));
11605                                 $this->setPageBuffer($startlinepage, $pstart."\nq\n".$trx."\n".$pmid."\nQ\n".$pend);
11606                                 $endlinepos = strlen($pstart."\nq\n".$trx."\n".$pmid."\nQ\n");
11607                                 // shift the annotations and links
11608                                 if (isset($this->PageAnnots[$this->page])) {
11609                                     foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
11610                                         if ($pak >= $pask) {
11611                                             $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
11612                                             $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
11613                                         }
11614                                     }
11615                                 }
11616                                 $this->y -= $yshift;
11617                             }
11618                         }
11619                     }
11620                     $this->newline = false;
11621                     $pbrk = $this->checkPageBreak($this->lasth);
11622                     $this->SetFont($fontname, $fontstyle, $fontsize);
11623                     if ($wfill) {
11624                         $this->SetFillColorArray($this->bgcolor);
11625                     }
11626                     $startlinex = $this->x;
11627                     $startliney = $this->y;
11628                     $minstartliney = $this->y;
11629                     $startlinepage = $this->page;
11630                     if (isset($endlinepos) AND (!$pbrk)) {
11631                         $startlinepos = $endlinepos;
11632                         unset($endlinepos);
11633                     } else {
11634                         if (isset($this->footerlen[$this->page])) {
11635                             $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
11636                         } else {
11637                             $this->footerpos[$this->page] = $this->pagelen[$this->page];
11638                         }
11639                         $startlinepos = $this->footerpos[$this->page];
11640                     }
11641                     $plalign = $lalign;
11642                     if (isset($this->PageAnnots[$this->page])) {
11643                         $pask = count($this->PageAnnots[$this->page]);
11644                     } else {
11645                         $pask = 0;
11646                     }
11647                 }
11648                 if (isset($opentagpos)) {
11649                     unset($opentagpos);
11650                 }
11651                 if ($dom[$key]['tag']) {
11652                     if ($dom[$key]['opening']) {
11653                         if ($dom[$key]['value'] == 'table') {
11654                             if ($this->rtl) {
11655                                 $wtmp = $this->x - $this->lMargin;
11656                             } else {
11657                                 $wtmp = $this->w - $this->rMargin - $this->x;
11658                             }
11659                             $wtmp -= (2 * $this->cMargin);
11660                             // calculate cell width
11661                             if (isset($dom[$key]['width'])) {
11662                                 $table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px');
11663                             } else {
11664                                 $table_width = $wtmp;
11665                             }
11666                         }
11667                         // table content is handled in a special way
11668                         if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
11669                             $trid = $dom[$key]['parent'];
11670                             $table_el = $dom[$trid]['parent'];
11671                             if (!isset($dom[$table_el]['cols'])) {
11672                                 $dom[$table_el]['cols'] = $trid['cols'];
11673                             }
11674                             $oldmargin = $this->cMargin;
11675                             if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) {
11676                                 $currentcmargin = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px');
11677                             } else {
11678                                 $currentcmargin = 0;        
11679                             }
11680                             $this->cMargin = $currentcmargin;
11681                             if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellspacing'])) {
11682                                 $cellspacing = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellspacing'], 1, 'px');
11683                             } else {
11684                                 $cellspacing = 0;
11685                             }
11686                             if ($this->rtl) {
11687                                 $cellspacingx = -$cellspacing;
11688                             } else {
11689                                 $cellspacingx = $cellspacing;
11690                             }
11691                             $colspan = $dom[$key]['attribute']['colspan'];
11692                             $wtmp = ($colspan * ($table_width / $dom[$table_el]['cols']));
11693                             if (isset($dom[$key]['width'])) {
11694                                 $cellw = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px');
11695                             } else {
11696                                 $cellw = $wtmp;
11697                             }
11698                             if (isset($dom[$key]['height'])) {
11699                                 // minimum cell height
11700                                 $cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px');
11701                             } else {
11702                                 $cellh = 0;
11703                             }
11704                             $cellw -= $cellspacing;
11705                             if (isset($dom[$key]['content'])) {
11706                                 $cell_content = $dom[$key]['content'];
11707                             } else {
11708                                 $cell_content = '&nbsp;';
11709                             }
11710                             $tagtype = $dom[$key]['value'];
11711                             $parentid = $key;
11712                             while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) {
11713                                 // move $key index forward
11714                                 ++$key;
11715                             }
11716                             if (!isset($dom[$trid]['startpage'])) {
11717                                 $dom[$trid]['startpage'] = $this->page;
11718                             } else {
11719                                 $this->setPage($dom[$trid]['startpage']);
11720                             }
11721                             if (!isset($dom[$trid]['starty'])) {
11722                                 $dom[$trid]['starty'] = $this->y;
11723                             } else {
11724                                 $this->y = $dom[$trid]['starty'];
11725                             }
11726                             if (!isset($dom[$trid]['startx'])) {
11727                                 $dom[$trid]['startx'] = $this->x;
11728                             }
11729                             $this->x += ($cellspacingx / 2);                        
11730                             if (isset($dom[$parentid]['attribute']['rowspan'])) {
11731                                 $rowspan = intval($dom[$parentid]['attribute']['rowspan']);
11732                             } else {
11733                                 $rowspan = 1;
11734                             }
11735                             // skip row-spanned cells started on the previous rows
11736                             if (isset($dom[$table_el]['rowspans'])) {
11737                                 $rsk = 0;
11738                                 $rskmax = count($dom[$table_el]['rowspans']);
11739                                 while ($rsk < $rskmax) {
11740                                     $trwsp = $dom[$table_el]['rowspans'][$rsk];
11741                                     $rsstartx = $trwsp['startx'];
11742                                     $rsendx = $trwsp['endx'];
11743                                     // account for margin changes
11744                                     if ($trwsp['startpage'] < $this->page) {
11745                                         if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$trwsp['startpage']]['orm'])) {
11746                                             $dl = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$trwsp['startpage']]['orm']);
11747                                             $rsstartx -= $dl;
11748                                             $rsendx -= $dl;
11749                                         } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$trwsp['startpage']]['olm'])) {
11750                                             $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$trwsp['startpage']]['olm']);
11751                                             $rsstartx += $dl;
11752                                             $rsendx += $dl;
11753                                         }
11754                                     }
11755                                     if  (($trwsp['rowspan'] > 0)
11756                                         AND ($rsstartx > ($this->x - $cellspacing - $currentcmargin - $this->feps))
11757                                         AND ($rsstartx < ($this->x + $cellspacing + $currentcmargin + $this->feps))
11758                                         AND (($trwsp['starty'] < ($this->y - $this->feps)) OR ($trwsp['startpage'] < $this->page))) {
11759                                         // set the starting X position of the current cell
11760                                         $this->x = $rsendx + $cellspacingx;
11761                                         if (($trwsp['rowspan'] == 1)
11762                                             AND (isset($dom[$trid]['endy']))
11763                                             AND (isset($dom[$trid]['endpage']))
11764                                             AND ($trwsp['endpage'] == $dom[$trid]['endpage'])) {
11765                                             // set ending Y position for row
11766                                             $dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
11767                                             $dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy'];
11768                                         }
11769                                         $rsk = 0;
11770                                     } else {
11771                                         ++$rsk;
11772                                     }
11773                                 }
11774                             }
11775                             // add rowspan information to table element
11776                             if ($rowspan > 1) {
11777                                 if (isset($this->footerlen[$this->page])) {
11778                                     $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
11779                                 } else {
11780                                     $this->footerpos[$this->page] = $this->pagelen[$this->page];
11781                                 }
11782                                 $trintmrkpos = $this->footerpos[$this->page];
11783                                 $trsid = array_push($dom[$table_el]['rowspans'], array('trid' => $trid, 'rowspan' => $rowspan, 'mrowspan' => $rowspan, 'colspan' => $colspan, 'startpage' => $this->page, 'startx' => $this->x, 'starty' => $this->y, 'intmrkpos' => $trintmrkpos));
11784                             }
11785                             $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x));
11786                             if ($rowspan > 1) {
11787                                 $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1);
11788                             }
11789                             // push background colors
11790                             if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) {
11791                                 $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor'];
11792                             }
11793                             $prevLastH = $this->lasth;
11794                             // ****** write the cell content ******
11795                             $this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true);
11796                             $this->lasth = $prevLastH;
11797                             $this->cMargin = $oldmargin;
11798                             $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x;
11799                             // update the end of row position
11800                             if ($rowspan <= 1) {
11801                                 if (isset($dom[$trid]['endy'])) {
11802                                     if ($this->page == $dom[$trid]['endpage']) {
11803                                         $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']);
11804                                     } elseif ($this->page > $dom[$trid]['endpage']) {
11805                                         $dom[$trid]['endy'] = $this->y;
11806                                     }
11807                                 } else {
11808                                     $dom[$trid]['endy'] = $this->y;
11809                                 }
11810                                 if (isset($dom[$trid]['endpage'])) {
11811                                     $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']);
11812                                 } else {
11813                                     $dom[$trid]['endpage'] = $this->page;
11814                                 }                               
11815                             } else {
11816                                 // account for row-spanned cells
11817                                 $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x;
11818                                 $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y;
11819                                 $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page;
11820                             }
11821                             if (isset($dom[$table_el]['rowspans'])) {
11822                                 // update endy and endpage on rowspanned cells
11823                                 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
11824                                     if ($trwsp['rowspan'] > 0) {
11825                                         if (isset($dom[$trid]['endpage'])) {
11826                                             if ($trwsp['endpage'] == $dom[$trid]['endpage']) {
11827                                                 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
11828                                             } elseif ($trwsp['endpage'] < $dom[$trid]['endpage']) {
11829                                                 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy'];
11830                                                 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage'];
11831                                             } else {
11832                                                 $dom[$trid]['endy'] = $this->pagedim[$dom[$trid]['endpage']]['hk'] - $this->pagedim[$dom[$trid]['endpage']]['bm'];
11833                                             }
11834                                         }
11835                                     }
11836                                 }
11837                             }
11838                             $this->x += ($cellspacingx / 2);                            
11839                         } else {
11840                             // opening tag (or self-closing tag)
11841                             if (!isset($opentagpos)) {
11842                                 if (!$this->InFooter) {
11843                                     if (isset($this->footerlen[$this->page])) {
11844                                         $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
11845                                     } else {
11846                                         $this->footerpos[$this->page] = $this->pagelen[$this->page];
11847                                     }
11848                                     $opentagpos = $this->footerpos[$this->page];
11849                                 }
11850                             }
11851                             $this->openHTMLTagHandler($dom, $key, $cell);
11852                         }
11853                     } else {
11854                         // closing tag
11855                         $this->closeHTMLTagHandler($dom, $key, $cell);
11856                     }
11857                 } elseif (strlen($dom[$key]['value']) > 0) {
11858                     // print list-item
11859                     if (!$this->empty_string($this->lispacer)) {
11860                         $this->SetFont($pfontname, $pfontstyle, $pfontsize);
11861                         $this->lasth = $this->FontSize * $this->cell_height_ratio;
11862                         $minstartliney = $this->y;
11863                         $this->putHtmlListBullet($this->listnum, $this->lispacer, $pfontsize);
11864                         $this->SetFont($curfontname, $curfontstyle, $curfontsize);
11865                         $this->lasth = $this->FontSize * $this->cell_height_ratio;
11866                         if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) {
11867                             $this->y += (($pfontsize - $curfontsize) / $this->k);
11868                             $minstartliney = min($this->y, $minstartliney);
11869                         }
11870                     }
11871                     // text
11872                     $this->htmlvspace = 0;
11873                     if ((!$this->premode) AND ($this->rtl OR $this->tmprtl)) {
11874                         // reverse spaces order
11875                         $len1 = strlen($dom[$key]['value']);
11876                         $lsp = $len1 - strlen(ltrim($dom[$key]['value']));
11877                         $rsp = $len1 - strlen(rtrim($dom[$key]['value']));
11878                         $tmpstr = '';
11879                         if ($rsp > 0) {
11880                             $tmpstr .= substr($dom[$key]['value'], -$rsp);
11881                         }
11882                         $tmpstr .= trim($dom[$key]['value']);
11883                         if ($lsp > 0) {
11884                             $tmpstr .= substr($dom[$key]['value'], 0, $lsp);
11885                         }
11886                         $dom[$key]['value'] = $tmpstr;
11887                     }
11888                     if ($newline) {
11889                         if (!$this->premode) {
11890                             if (($this->rtl OR $this->tmprtl)) {
11891                                 $dom[$key]['value'] = rtrim($dom[$key]['value']);
11892                             } else {
11893                                 $dom[$key]['value'] = ltrim($dom[$key]['value']);
11894                             }
11895                         }
11896                         $newline = false;
11897                         $firstblock = true;
11898                     } else {
11899                         $firstblock = false;
11900                     }
11901                     $strrest = '';
11902                     if (!empty($this->HREF) AND (isset($this->HREF['url']))) {
11903                         // HTML <a> Link
11904                         $strrest = $this->addHtmlLink($this->HREF['url'], $dom[$key]['value'], $wfill, true, $this->HREF['color'], $this->HREF['style']);
11905                     } else {
11906                         $ctmpmargin = $this->cMargin;
11907                         $this->cMargin = 0;
11908                         // ****** write only until the end of the line and get the rest ******
11909                         $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock);
11910                         $this->cMargin = $ctmpmargin;
11911                     }
11912                     if (strlen($strrest) > 0) {
11913                         // store the remaining string on the previous $key position
11914                         $this->newline = true;
11915                         if ($cell) {
11916                             if ($this->rtl) {
11917                                 $this->x -= $this->cMargin;
11918                             } else {
11919                                 $this->x += $this->cMargin;
11920                             }
11921                         }
11922                         if ($strrest == $dom[$key]['value']) {
11923                             // used to avoid infinite loop
11924                             ++$loop;
11925                         } else {
11926                             $loop = 0;
11927                         }
11928                         $dom[$key]['value'] = ltrim($strrest);
11929                         if ($loop < 3) {
11930                             --$key;
11931                         }
11932                     } else {
11933                         $loop = 0;
11934                     }
11935                 }
11936                 ++$key;
11937             } // end for each $key
11938             // align the last line
11939             if (isset($startlinex)) {
11940                 $yshift = $minstartliney - $startliney;
11941                 if (($yshift > 0) OR ($this->page > $startlinepage)) {
11942                     $yshift = 0;
11943                 }
11944                 if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl))))) OR ($yshift < 0)) {
11945                     // the last line must be shifted to be aligned as requested
11946                     $linew = abs($this->endlinex - $startlinex);
11947                     $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
11948                     if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
11949                         $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
11950                         $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
11951                     } elseif (isset($opentagpos)) {
11952                         $midpos = $opentagpos;
11953                     } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
11954                         $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
11955                         $midpos = $this->footerpos[$startlinepage];
11956                     } else {
11957                         $midpos = 0;
11958                     }
11959                     if ($midpos > 0) {
11960                         $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
11961                         $pend = substr($this->getPageBuffer($startlinepage), $midpos);
11962                     } else {
11963                         $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
11964                         $pend = '';
11965                     }   
11966                     // calculate shifting amount
11967                     $tw = $w;
11968                     if ($this->lMargin != $prevlMargin) {
11969                         $tw += ($prevlMargin - $this->lMargin);
11970                     }
11971                     if ($this->rMargin != $prevrMargin) {
11972                         $tw += ($prevrMargin - $this->rMargin);
11973                     }
11974                     $mdiff = abs($tw - $linew);
11975                     if ($plalign == 'C') {
11976                         if ($this->rtl) {
11977                             $t_x = -($mdiff / 2);
11978                         } else {
11979                             $t_x = ($mdiff / 2);
11980                         }
11981                     } elseif (($plalign == 'R') AND (!$this->rtl)) {
11982                         // right alignment on LTR document
11983                         $t_x = $mdiff;
11984                     } elseif (($plalign == 'L') AND ($this->rtl)) {
11985                         // left alignment on RTL document
11986                         $t_x = -$mdiff;
11987                     } else {
11988                         $t_x = 0;
11989                     }
11990                     if (($t_x != 0) OR ($yshift < 0)) {
11991                         // shift the line
11992                         $trx = sprintf('1 0 0 1 %.3F %.3F cm', ($t_x * $this->k), ($yshift * $this->k));
11993                         $this->setPageBuffer($startlinepage, $pstart."\nq\n".$trx."\n".$pmid."\nQ\n".$pend);
11994                         $endlinepos = strlen($pstart."\nq\n".$trx."\n".$pmid."\nQ\n");
11995                         // shift the annotations and links
11996                         if (isset($this->PageAnnots[$this->page])) {
11997                             foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
11998                                 if ($pak >= $pask) {
11999                                     $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
12000                                     $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
12001                                 }
12002                             }
12003                         }
12004                         $this->y -= $yshift;
12005                     }
12006                 }
12007             }
12008             if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) {
12009                 $this->Ln($this->lasth);
12010             }
12011             // restore previous values
12012             $this->setGraphicVars($gvars);
12013             if ($this->page > $prevPage) {
12014                 $this->lMargin = $this->pagedim[$this->page]['olm'];
12015                 $this->rMargin = $this->pagedim[$this->page]['orm'];
12016             }
12017             unset($dom);
12018         }
12019         
12027         protected function openHTMLTagHandler(&$dom, $key, $cell=false) {
12028             $tag = $dom[$key];
12029             $parent = $dom[($dom[$key]['parent'])];
12030             $firstorlast = ($key == 1);
12031             // check for text direction attribute
12032             if (isset($tag['attribute']['dir'])) {
12033                 $this->tmprtl = $tag['attribute']['dir'] == 'rtl' ? 'R' : 'L';
12034             } else {
12035                 $this->tmprtl = false;
12036             }
12037             //Opening tag
12038             switch($tag['value']) {
12039                 case 'table': {
12040                     $cp = 0;
12041                     $cs = 0;
12042                     $dom[$key]['rowspans'] = array();
12043                     if (!$this->empty_string($dom[$key]['thead'])) {
12044                         // set table header
12045                         $this->thead = $dom[$key]['thead'];
12046                     }
12047                     if (isset($tag['attribute']['cellpadding'])) {
12048                         $cp = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px');
12049                         $this->oldcMargin = $this->cMargin;
12050                         $this->cMargin = $cp;
12051                     }
12052                     if (isset($tag['attribute']['cellspacing'])) {
12053                         $cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
12054                     }
12055                     $this->checkPageBreak((2 * $cp) + (2 * $cs) + $this->lasth);
12056                     break;
12057                 }
12058                 case 'tr': {
12059                     // array of columns positions
12060                     $dom[$key]['cellpos'] = array();
12061                     break;
12062                 }
12063                 case 'hr': {
12064                     $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12065                     $this->htmlvspace = 0;
12066                     $wtmp = $this->w - $this->lMargin - $this->rMargin;
12067                     if ((isset($tag['attribute']['width'])) AND ($tag['attribute']['width'] != '')) {
12068                         $hrWidth = $this->getHTMLUnitToUnits($tag['attribute']['width'], $wtmp, 'px');
12069                     } else {
12070                         $hrWidth = $wtmp;
12071                     }
12072                     $x = $this->GetX();
12073                     $y = $this->GetY();
12074                     $prevlinewidth = $this->GetLineWidth();
12075                     $this->Line($x, $y, $x + $hrWidth, $y);
12076                     $this->SetLineWidth($prevlinewidth);
12077                     $this->addHTMLVertSpace(1, $cell, '', !isset($dom[($key + 1)]), $tag['value'], false);
12078                     break;
12079                 }
12080                 case 'a': {
12081                     if (array_key_exists('href', $tag['attribute'])) {
12082                         $this->HREF['url'] = $tag['attribute']['href'];
12083                     }
12084                     $this->HREF['color'] = $this->htmlLinkColorArray;
12085                     $this->HREF['style'] = $this->htmlLinkFontStyle;
12086                     if (array_key_exists('style', $tag['attribute'])) {
12087                         // get style attributes
12088                         preg_match_all('/([^;:\s]*):([^;]*)/', $tag['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
12089                         $astyle = array();
12090                         while (list($id, $name) = each($style_array[1])) {
12091                             $name = strtolower($name);
12092                             $astyle[$name] = trim($style_array[2][$id]);
12093                         }
12094                         if (isset($astyle['color'])) {
12095                             $this->HREF['color'] = $this->convertHTMLColorToDec($astyle['color']);
12096                         }
12097                         if (isset($astyle['text-decoration'])) {
12098                             $this->HREF['style'] = '';
12099                             $decors = explode(' ', strtolower($astyle['text-decoration']));
12100                             foreach ($decors as $dec) {
12101                                 $dec = trim($dec);
12102                                 if (!$this->empty_string($dec)) {
12103                                     if ($dec{0} == 'u') {
12104                                         $this->HREF['style'] .= 'U';
12105                                     } elseif ($dec{0} == 'l') {
12106                                         $this->HREF['style'] .= 'D';
12107                                     }
12108                                 }
12109                             }
12110                         }
12111                     }       
12112                     break;
12113                 }
12114                 case 'img': {
12115                     if (isset($tag['attribute']['src'])) {
12116                         // replace relative path with real server path
12117                         if ($tag['attribute']['src'][0] == '/') {
12118                             $tag['attribute']['src'] = $_SERVER['DOCUMENT_ROOT'].$tag['attribute']['src'];
12119                         }
12120                         $tag['attribute']['src'] = urldecode($tag['attribute']['src']);
12121                         $tag['attribute']['src'] = str_replace(K_PATH_URL, K_PATH_MAIN, $tag['attribute']['src']);
12122                         if (!isset($tag['attribute']['width'])) {
12123                             $tag['attribute']['width'] = 0;
12124                         }
12125                         if (!isset($tag['attribute']['height'])) {
12126                             $tag['attribute']['height'] = 0;
12127                         }
12128                         //if (!isset($tag['attribute']['align'])) {
12129                             // the only alignment supported is "bottom"
12130                             // further development is required for other modes.
12131                             $tag['attribute']['align'] = 'bottom';
12132                         //} 
12133                         switch($tag['attribute']['align']) {
12134                             case 'top': {
12135                                 $align = 'T';
12136                                 break;
12137                             }
12138                             case 'middle': {
12139                                 $align = 'M';
12140                                 break;
12141                             }
12142                             case 'bottom': {
12143                                 $align = 'B';
12144                                 break;
12145                             }
12146                             default: {
12147                                 $align = 'B';
12148                                 break;
12149                             }
12150                         }
12151                         $fileinfo = pathinfo($tag['attribute']['src']);
12152                         if (isset($fileinfo['extension']) AND (!$this->empty_string($fileinfo['extension']))) {
12153                             $type = strtolower($fileinfo['extension']);
12154                         }
12155                         $prevy = $this->y;
12156                         $xpos = $this->GetX();
12157                         if (isset($dom[($key - 1)]) AND ($dom[($key - 1)]['value'] == ' ')) {
12158                             if ($this->rtl) {
12159                                 $xpos += $this->GetStringWidth(' ');
12160                             } else {
12161                                 $xpos -= $this->GetStringWidth(' ');
12162                             }
12163                         }
12164                         $imglink = '';
12165                         if (isset($this->HREF['url']) AND !$this->empty_string($this->HREF['url'])) {
12166                             $imglink = $this->HREF['url'];
12167                             if ($imglink{0} == '#') {
12168                                 // convert url to internal link
12169                                 $page = intval(substr($imglink, 1));
12170                                 $imglink = $this->AddLink();
12171                                 $this->SetLink($imglink, 0, $page);
12172                             }
12173                         }
12174                         $border = 0;
12175                         if (isset($tag['attribute']['border']) AND !empty($tag['attribute']['border'])) {
12176                             // currently only support 1 (frame) or a combination of 'LTRB'
12177                             $border = $tag['attribute']['border'];
12178                         }
12179                         if (isset($tag['attribute']['width'])) {
12180                             $iw = $this->getHTMLUnitToUnits($tag['attribute']['width'], 1, 'px', false);
12181                         }
12182                         if (isset($tag['attribute']['height'])) {
12183                             $ih = $this->getHTMLUnitToUnits($tag['attribute']['height'], 1, 'px', false);
12184                         }
12185                         if (($type == 'eps') OR ($type == 'ai')) {
12186                             $this->ImageEps($tag['attribute']['src'], $xpos, $this->GetY(), $iw, $ih, $imglink, true, $align, '', $border);
12187                         } else {
12188                             $this->Image($tag['attribute']['src'], $xpos, $this->GetY(), $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border);
12189                         }
12190                         switch($align) {
12191                             case 'T': {
12192                                 $this->y = $prevy;
12193                                 break;
12194                             }
12195                             case 'M': {
12196                                 $this->y = (($this->img_rb_y + $prevy - ($tag['fontsize'] / $this->k)) / 2) ;
12197                                 break;
12198                             }
12199                             case 'B': {
12200                                 $this->y = $this->img_rb_y - ($tag['fontsize'] / $this->k);
12201                                 break;
12202                             }
12203                         }
12204                     }
12205                     break;
12206                 }
12207                 case 'dl': {
12208                     ++$this->listnum;
12209                     $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], false);
12210                     break;
12211                 }
12212                 case 'dt': {
12213                     $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12214                     break;
12215                 }
12216                 case 'dd': {
12217                     if ($this->rtl) {
12218                         $this->rMargin += $this->listindent;
12219                     } else {
12220                         $this->lMargin += $this->listindent;
12221                     }
12222                     $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12223                     break;
12224                 }
12225                 case 'ul':
12226                 case 'ol': {
12227                     $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], false);
12228                     $this->htmlvspace = 0;
12229                     ++$this->listnum;
12230                     if ($tag['value'] == 'ol') {
12231                         $this->listordered[$this->listnum] = true;
12232                     } else {
12233                         $this->listordered[$this->listnum] = false;
12234                     }
12235                     if (isset($tag['attribute']['start'])) {
12236                         $this->listcount[$this->listnum] = intval($tag['attribute']['start']) - 1;
12237                     } else {
12238                         $this->listcount[$this->listnum] = 0;
12239                     }
12240                     if ($this->rtl) {
12241                         $this->rMargin += $this->listindent;
12242                     } else {
12243                         $this->lMargin += $this->listindent;
12244                     }
12245                     $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], false);
12246                     $this->htmlvspace = 0;
12247                     break;
12248                 }
12249                 case 'li': {
12250                     $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12251                     if ($this->listordered[$this->listnum]) {
12252                         // ordered item
12253                         if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) {
12254                             $this->lispacer = $parent['attribute']['type'];
12255                         } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) {
12256                             $this->lispacer = $parent['listtype'];
12257                         } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) {
12258                             $this->lispacer = $this->lisymbol;
12259                         } else {
12260                             $this->lispacer = '#';
12261                         }
12262                         ++$this->listcount[$this->listnum];
12263                         if (isset($tag['attribute']['value'])) {
12264                             $this->listcount[$this->listnum] = intval($tag['attribute']['value']);
12265                         }
12266                     } else {
12267                         // unordered item
12268                         if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) {
12269                             $this->lispacer = $parent['attribute']['type'];
12270                         } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) {
12271                             $this->lispacer = $parent['listtype'];
12272                         } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) {
12273                             $this->lispacer = $this->lisymbol;
12274                         } else {
12275                             $this->lispacer = '!';
12276                         }
12277                     }
12278                     break;
12279                 }
12280                 case 'blockquote': {
12281                     if ($this->rtl) {
12282                         $this->rMargin += $this->listindent;
12283                     } else {
12284                         $this->lMargin += $this->listindent;
12285                     }
12286                     $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], false);
12287                     break;
12288                 }
12289                 case 'br': {
12290                     $this->Ln('', $cell);
12291                     break;
12292                 }
12293                 case 'div': {
12294                     $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12295                     break;
12296                 }
12297                 case 'p': {
12298                     $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], false);
12299                     break;
12300                 }
12301                 case 'pre': {
12302                     $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12303                     $this->premode = true;
12304                     break;
12305                 }
12306                 case 'sup': {
12307                     $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k));
12308                     break;
12309                 }
12310                 case 'sub': {
12311                     $this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k));
12312                     break;
12313                 }
12314                 case 'h1': 
12315                 case 'h2': 
12316                 case 'h3': 
12317                 case 'h4': 
12318                 case 'h5': 
12319                 case 'h6': {
12320                     $this->addHTMLVertSpace(1, $cell, ($tag['fontsize'] * 1.5) / $this->k, $firstorlast, $tag['value'], false);
12321                     break;
12322                 }
12323                 case 'tcpdf': {
12324                     // NOT HTML: used to call TCPDF methods
12325                     if (isset($tag['attribute']['method'])) {
12326                         $tcpdf_method = $tag['attribute']['method'];
12327                         if (method_exists($this, $tcpdf_method)) {
12328                             if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) {
12329                                 eval('$params = array('.$tag['attribute']['params'].');');
12330                                 call_user_func_array(array($this, $tcpdf_method), $params);
12331                             } else {
12332                                 $this->$tcpdf_method();
12333                             }
12334                             $this->newline = true;
12335                         }
12336                     }
12337                 }
12338                 default: {
12339                     break;
12340                 }
12341             }
12342         }
12343         
12351         protected function closeHTMLTagHandler(&$dom, $key, $cell=false) {
12352             $tag = $dom[$key];
12353             $parent = $dom[($dom[$key]['parent'])];
12354             $firstorlast = ((!isset($dom[($key + 1)])) OR ((!isset($dom[($key + 2)])) AND ($dom[($key + 1)]['value'] == 'marker')));
12355             //Closing tag
12356             switch($tag['value']) {
12357                 case 'tr': {
12358                     $table_el = $dom[($dom[$key]['parent'])]['parent'];
12359                     if(!isset($parent['endy'])) {
12360                         $dom[($dom[$key]['parent'])]['endy'] = $this->y;
12361                         $parent['endy'] = $this->y;
12362                     }
12363                     if(!isset($parent['endpage'])) {
12364                         $dom[($dom[$key]['parent'])]['endpage'] = $this->page;
12365                         $parent['endpage'] = $this->page;
12366                     }
12367                     // update row-spanned cells
12368                     if (isset($dom[$table_el]['rowspans'])) {
12369                         foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
12370                             $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1;
12371                             if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
12372                                 if ($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) {
12373                                     $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']);
12374                                 } elseif ($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) {
12375                                     $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
12376                                     $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
12377                                 }
12378                             }
12379                         }
12380                         // report new endy and endpage to the rowspanned cells
12381                         foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
12382                             if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
12383                                 $dom[$table_el]['rowspans'][$k]['endpage'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']);
12384                                 $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
12385                                 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']);
12386                                 $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
12387                             }
12388                         }
12389                         // update remaining rowspanned cells
12390                         foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
12391                             if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
12392                                 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage'];
12393                                 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy'];
12394                             }
12395                         }
12396                     }
12397                     $this->setPage($dom[($dom[$key]['parent'])]['endpage']);
12398                     $this->y = $dom[($dom[$key]['parent'])]['endy'];                    
12399                     if (isset($dom[$table_el]['attribute']['cellspacing'])) {
12400                         $cellspacing = $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px');
12401                         $this->y += $cellspacing;
12402                     }               
12403                     $this->Ln(0, $cell);
12404                     $this->x = $parent['startx'];
12405                     // account for booklet mode
12406                     if ($this->page > $parent['startpage']) {
12407                         if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$parent['startpage']]['orm'])) {
12408                             $this->x += ($this->pagedim[$this->page]['orm'] - $this->pagedim[$parent['startpage']]['orm']);
12409                         } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$parent['startpage']]['olm'])) {
12410                             $this->x += ($this->pagedim[$this->page]['olm'] - $this->pagedim[$parent['startpage']]['olm']);
12411                         }
12412                     }
12413                     break;
12414                 }
12415                 case 'table': {
12416                     // draw borders
12417                     $table_el = $parent;
12418                     if ((isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0)) 
12419                         OR (isset($table_el['style']['border']) AND ($table_el['style']['border'] > 0))) {
12420                             $border = 1;
12421                     } else {
12422                         $border = 0;
12423                     }
12424                     // fix bottom line alignment of last line before page break
12425                     foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) {
12426                         // update row-spanned cells
12427                         if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
12428                             foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
12429                                 if ($trwsp['trid'] == $trkey) {
12430                                     $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1;
12431                                 }
12432                                 if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] >= 0)) {
12433                                     $dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey;
12434                                 }
12435                             }
12436                         }
12437                         if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) {
12438                             $pgendy = $this->pagedim[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim[$dom[$prevtrkey]['endpage']]['bm'];
12439                             $dom[$prevtrkey]['endy'] = $pgendy;
12440                             // update row-spanned cells
12441                             if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
12442                                 foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
12443                                     if (($trwsp['trid'] == $trkey) AND ($trwsp['mrowspan'] == 1) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) {
12444                                         $dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy;
12445                                         $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1;
12446                                     }
12447                                 }
12448                             }
12449                         }
12450                         $prevtrkey = $trkey;
12451                         $table_el = $dom[($dom[$key]['parent'])];
12452                     }
12453                     // for each row
12454                     foreach ($table_el['trids'] as $j => $trkey) {
12455                         $parent = $dom[$trkey];
12456                         // for each cell on the row
12457                         foreach ($parent['cellpos'] as $k => $cellpos) {
12458                             if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) {
12459                                 $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx'];
12460                                 $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx'];
12461                                 $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy'];
12462                                 $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage'];
12463                                 $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage'];
12464                             } else {
12465                                 $endy = $parent['endy'];
12466                                 $startpage = $parent['startpage'];
12467                                 $endpage = $parent['endpage'];
12468                             }
12469                             if ($endpage > $startpage) {
12470                                 // design borders around HTML cells.
12471                                 for ($page=$startpage; $page <= $endpage; ++$page) {
12472                                     $this->setPage($page);
12473                                     if ($page == $startpage) {
12474                                         $this->y = $parent['starty']; // put cursor at the beginning of row on the first page
12475                                         $ch = $this->getPageHeight() - $parent['starty'] - $this->getBreakMargin();
12476                                         $cborder = $this->getBorderMode($border, $position='start');
12477                                     } elseif ($page == $endpage) {
12478                                         $this->y = $this->tMargin; // put cursor at the beginning of last page
12479                                         $ch = $endy - $this->tMargin;
12480                                         $cborder = $this->getBorderMode($border, $position='end');
12481                                     } else {
12482                                         $this->y = $this->tMargin; // put cursor at the beginning of the current page
12483                                         $ch = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin();
12484                                         $cborder = $this->getBorderMode($border, $position='middle');
12485                                     }
12486                                     if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
12487                                         $this->SetFillColorArray($cellpos['bgcolor']);
12488                                         $fill = true;
12489                                     } else {
12490                                         $fill = false;
12491                                     }
12492                                     $cw = abs($cellpos['endx'] - $cellpos['startx']);
12493                                     $this->x = $cellpos['startx'];
12494                                     // account for margin changes
12495                                     if ($page > $startpage) {
12496                                         if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
12497                                             $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
12498                                         } elseif ((!$this->rtl) AND ($this->pagedim[$page]['lm'] != $this->pagedim[$startpage]['olm'])) {
12499                                             $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
12500                                         }
12501                                     }
12502                                     // design a cell around the text
12503                                     $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, '', $cborder, 1, '', $fill, '', 0, true);
12504                                     if ($cborder OR $fill) {
12505                                         $pagebuff = $this->getPageBuffer($this->page);
12506                                         $pstart = substr($pagebuff, 0, $this->intmrk[$this->page]);
12507                                         $pend = substr($pagebuff, $this->intmrk[$this->page]);
12508                                         $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend);
12509                                         $this->intmrk[$this->page] += strlen($ccode."\n");
12510                                     }
12511                                 }
12512                             } else {
12513                                 $this->setPage($startpage);
12514                                 if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
12515                                     $this->SetFillColorArray($cellpos['bgcolor']);
12516                                     $fill = true;
12517                                 } else {
12518                                     $fill = false;
12519                                 }
12520                                 $this->x = $cellpos['startx'];
12521                                 $this->y = $parent['starty'];
12522                                 $cw = abs($cellpos['endx'] - $cellpos['startx']);
12523                                 $ch = $endy - $parent['starty'];
12524                                 // design a cell around the text
12525                                 $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, '', $border, 1, '', $fill, '', 0, true);
12526                                 if ($border OR $fill) {
12527                                     if (end($this->transfmrk[$this->page]) !== false) {
12528                                         $pagemarkkey = key($this->transfmrk[$this->page]);
12529                                         $pagemark = &$this->transfmrk[$this->page][$pagemarkkey];
12530                                     } elseif ($this->InFooter) {
12531                                         $pagemark = &$this->footerpos[$this->page];
12532                                     } else {
12533                                         $pagemark = &$this->intmrk[$this->page];
12534                                     }
12535                                     $pagebuff = $this->getPageBuffer($this->page);
12536                                     $pstart = substr($pagebuff, 0, $pagemark);
12537                                     $pend = substr($pagebuff, $pagemark);
12538                                     $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend);
12539                                     $pagemark += strlen($ccode."\n");
12540                                 }                   
12541                             }
12542                         }                   
12543                         if (isset($table_el['attribute']['cellspacing'])) {
12544                             $cellspacing = $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px');
12545                             $this->y += $cellspacing;
12546                         }               
12547                         $this->Ln(0, $cell);
12548                         $this->x = $parent['startx'];
12549                         if ($endpage > $startpage) {
12550                             if (($this->rtl) AND ($this->pagedim[$endpage]['orm'] != $this->pagedim[$startpage]['orm'])) {
12551                                 $this->x += ($this->pagedim[$endpage]['orm'] - $this->pagedim[$startpage]['orm']);
12552                             } elseif ((!$this->rtl) AND ($this->pagedim[$endpage]['olm'] != $this->pagedim[$startpage]['olm'])) {
12553                                 $this->x += ($this->pagedim[$endpage]['olm'] - $this->pagedim[$startpage]['olm']);
12554                             }
12555                         }
12556                     }
12557                     if (isset($parent['cellpadding'])) {
12558                         $this->cMargin = $this->oldcMargin;
12559                     }
12560                     $this->lasth = $this->FontSize * $this->cell_height_ratio;
12561                     if (!$this->empty_string($this->theadMargin)) {
12562                         // restore top margin
12563                         $this->tMargin = $this->theadMargin;
12564                         $this->pagedim[$this->page]['tm'] = $this->theadMargin;
12565                     }
12566                     // reset table header
12567                     $this->thead = '';
12568                     $this->theadMargin = '';
12569                     break;
12570                 }
12571                 case 'a': {
12572                     $this->HREF = '';
12573                     break;
12574                 }
12575                 case 'sup': {
12576                     $this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k));
12577                     break;
12578                 }
12579                 case 'sub': {
12580                     $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize'])/$this->k));
12581                     break;
12582                 }
12583                 case 'div': {
12584                     $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], true);
12585                     break;
12586                 }
12587                 case 'blockquote': {
12588                     if ($this->rtl) {
12589                         $this->rMargin -= $this->listindent;
12590                     } else {
12591                         $this->lMargin -= $this->listindent;
12592                     }
12593                     $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true);
12594                     break;
12595                 }
12596                 case 'p': {
12597                     $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true);
12598                     break;
12599                 }
12600                 case 'pre': {
12601                     $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], true);
12602                     $this->premode = false;
12603                     break;
12604                 }
12605                 case 'dl': {
12606                     --$this->listnum;
12607                     if ($this->listnum <= 0) {
12608                         $this->listnum = 0;
12609                         $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true);
12610                     }
12611                     break;
12612                 }
12613                 case 'dt': {
12614                     $this->lispacer = '';
12615                     $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], true);
12616                     break;
12617                 }
12618                 case 'dd': {
12619                     $this->lispacer = '';
12620                     if ($this->rtl) {
12621                         $this->rMargin -= $this->listindent;
12622                     } else {
12623                         $this->lMargin -= $this->listindent;
12624                     }
12625                     $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], true);
12626                     break;
12627                 }
12628                 case 'ul':
12629                 case 'ol': {
12630                     --$this->listnum;
12631                     $this->lispacer = '';
12632                     if ($this->rtl) {
12633                         $this->rMargin -= $this->listindent;
12634                     } else {
12635                         $this->lMargin -= $this->listindent;
12636                     }
12637                     if ($this->listnum <= 0) {
12638                         $this->listnum = 0;
12639                         $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true);
12640                     }
12641                     $this->lasth = $this->FontSize * $this->cell_height_ratio;
12642                     break;
12643                 }
12644                 case 'li': {
12645                     $this->lispacer = '';
12646                     $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], true);
12647                     break;
12648                 }
12649                 case 'h1': 
12650                 case 'h2': 
12651                 case 'h3': 
12652                 case 'h4': 
12653                 case 'h5': 
12654                 case 'h6': {
12655                     $this->addHTMLVertSpace(1, $cell, ($parent['fontsize'] * 1.5) / $this->k, $firstorlast, $tag['value'], true);
12656                     break;
12657                 }
12658                 default : {
12659                     break;
12660                 }
12661             }
12662             $this->tmprtl = false;
12663         }
12664         
12675         protected function addHTMLVertSpace($n, $cell=false, $h='', $firstorlast=false, $tag='', $closing=false) {
12676             if ($firstorlast) {
12677                 $this->Ln(0, $cell);
12678                 $this->htmlvspace = 0;
12679                 return;
12680             }
12681             if (isset($this->tagvspaces[$tag][intval($closing)]['n'])) {
12682                 $n = $this->tagvspaces[$tag][intval($closing)]['n'];
12683             }
12684             if (isset($this->tagvspaces[$tag][intval($closing)]['h'])) {
12685                 $h = $this->tagvspaces[$tag][intval($closing)]['h'];
12686             }
12687             if (is_string($h)) {
12688                 $vsize = $n * $this->lasth;
12689             } else {
12690                 $vsize = $n * $h;
12691             }
12692             if ($vsize > $this->htmlvspace) {
12693                 $this->Ln(($vsize - $this->htmlvspace), $cell);
12694                 $this->htmlvspace = $vsize;
12695             }
12696         }
12697         
12704         public function setLIsymbol($symbol='!') {
12705             $symbol = strtolower($symbol);
12706             switch ($symbol) {
12707                 case '!' :
12708                 case '#' :
12709                 case 'disc' :
12710                 case 'disc' :
12711                 case 'circle' :
12712                 case 'square' :
12713                 case '1':
12714                 case 'decimal':
12715                 case 'decimal-leading-zero':
12716                 case 'i':
12717                 case 'lower-roman':
12718                 case 'I':
12719                 case 'upper-roman':
12720                 case 'a':
12721                 case 'lower-alpha':
12722                 case 'lower-latin':
12723                 case 'A':
12724                 case 'upper-alpha':
12725                 case 'upper-latin':
12726                 case 'lower-greek': {
12727                     $this->lisymbol = $symbol;
12728                     break;
12729                 }
12730                 default : {
12731                     $this->lisymbol = '';
12732                 }
12733             }
12734         }
12735         
12744         public function SetBooklet($booklet=true, $inner=-1, $outer=-1) {
12745             $this->booklet = $booklet;
12746             if ($inner >= 0) {
12747                 $this->lMargin = $inner;
12748             }
12749             if ($outer >= 0) {
12750                 $this->rMargin = $outer;
12751             }
12752         }
12753         
12760         protected function swapMargins($reverse=true) {
12761             if ($reverse) {
12762                 // swap left and right margins
12763                 $mtemp = $this->original_lMargin;
12764                 $this->original_lMargin = $this->original_rMargin;
12765                 $this->original_rMargin = $mtemp;
12766                 $deltam = $this->original_lMargin - $this->original_rMargin;
12767                 $this->lMargin += $deltam;
12768                 $this->rMargin -= $deltam;
12769             }
12770         }
12771 
12784         public function setHtmlVSpace($tagvs) {
12785             $this->tagvspaces = $tagvs;
12786         }
12787 
12794         public function setListIndentWidth($width) {
12795             return $this->customlistindent = floatval($width);
12796         }
12797 
12804         public function setOpenCell($isopen) {
12805             $this->opencell = $isopen;
12806         }
12807 
12815         public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') {
12816             $this->htmlLinkColorArray = $color;
12817             $this->htmlLinkFontStyle = $fontstyle;
12818         }
12819 
12830         public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) {
12831             $supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt');
12832             $retval = 0;
12833             $value = 0;
12834             $unit = 'px';
12835             $k = $this->k;
12836             if ($points) {
12837                 $k = 1;
12838             }
12839             if (in_array($defaultunit, $supportedunits)) {
12840                 $unit = $defaultunit;
12841             }
12842             if (is_numeric($htmlval)) {
12843                 $value = floatval($htmlval);
12844             } elseif (preg_match('/([0-9\.]+)/', $htmlval, $mnum)) {
12845                 $value = floatval($mnum[1]);
12846                 if (preg_match('/([a-z%]+)/', $htmlval, $munit)) {
12847                     if (in_array($munit[1], $supportedunits)) {
12848                         $unit = $munit[1];
12849                     }
12850                 }
12851             }
12852             switch ($unit) {
12853                 // percentage
12854                 case '%': {
12855                     $retval = (($value * $refsize) / 100);
12856                     break;
12857                 }
12858                 // relative-size
12859                 case 'em': {
12860                     $retval = ($value * $refsize);
12861                     break;
12862                 }
12863                 case 'ex': {
12864                     $retval = $value * ($refsize / 2);
12865                     break;
12866                 }
12867                 // absolute-size
12868                 case 'in': {
12869                     $retval = ($value * $this->dpi) / $k;
12870                     break;
12871                 }
12872                 case 'cm': {
12873                     $retval = ($value / 2.54 * $this->dpi) / $k;
12874                     break;
12875                 }
12876                 case 'mm': {
12877                     $retval = ($value / 25.4 * $this->dpi) / $k;
12878                     break;
12879                 }
12880                 case 'pc': {
12881                     // one pica is 12 points
12882                     $retval = ($value * 12) / $k;
12883                     break;
12884                 }
12885                 case 'pt': {
12886                     $retval = $value / $k;
12887                     break;
12888                 }
12889                 case 'px': {
12890                     $retval = $this->pixelsToUnits($value);
12891                     break;
12892                 }
12893             }
12894             return $retval;
12895         }
12896 
12904         public function intToRoman($number) {
12905             $roman = '';
12906             while ($number >= 1000) {
12907                 $roman .= 'M';
12908                 $number -= 1000;
12909             }
12910             while ($number >= 900) {
12911                 $roman .= 'CM';
12912                 $number -= 900;
12913             }
12914             while ($number >= 500) {
12915                 $roman .= 'D';
12916                 $number -= 500;
12917             }
12918             while ($number >= 400) {
12919                 $roman .= 'CD';
12920                 $number -= 400;
12921             }
12922             while ($number >= 100) {
12923                 $roman .= 'C';
12924                 $number -= 100;
12925             }
12926             while ($number >= 90) {
12927             $roman .= 'XC';
12928             $number -= 90;
12929             }
12930             while ($number >= 50) {
12931                 $roman .= 'L';
12932                 $number -= 50;
12933             }
12934             while ($number >= 40) {
12935                 $roman .= 'XL';
12936                 $number -= 40;
12937             }
12938             while ($number >= 10) {
12939             $roman .= 'X';
12940             $number -= 10;
12941             }
12942             while ($number >= 9) {
12943                 $roman .= 'IX';
12944                 $number -= 9;
12945             }
12946             while ($number >= 5) {
12947                 $roman .= 'V';
12948                 $number -= 5;
12949             }
12950             while ($number >= 4) {
12951             $roman .= 'IV';
12952             $number -= 4;
12953             }
12954             while ($number >= 1) {
12955                 $roman .= 'I';
12956                 --$number;
12957             }
12958             return $roman;
12959         }
12960 
12969         protected function putHtmlListBullet($listdepth, $listtype='', $size=10) {
12970             $size /= $this->k;
12971             $fill = '';
12972             $color = $this->fgcolor;
12973             $width = 0;
12974             $textitem = '';
12975             $tmpx = $this->x;       
12976             $lspace = $this->GetStringWidth('  ');
12977             if ($listtype == '!') {
12978                 // set default list type for unordered list
12979                 $deftypes = array('disc', 'circle', 'square');
12980                 $listtype = $deftypes[($listdepth - 1) % 3];
12981             } elseif ($listtype == '#') {
12982                 // set default list type for ordered list
12983                 $listtype = 'decimal';
12984             }
12985             switch ($listtype) {
12986                 // unordered types
12987                 case 'none': {
12988                     break;
12989                 }
12990                 case 'disc': {
12991                     $fill = 'F';
12992                 }
12993                 case 'circle': {
12994                     $fill .= 'D';
12995                     $r = $size / 6;
12996                     $lspace += (2 * $r);
12997                     if ($this->rtl) {
12998                         $this->x += $lspace;
12999                     } else {
13000                         $this->x -= $lspace;
13001                     }
13002                     $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), $r, 0, 360, $fill, array('color'=>$color), $color, 8);
13003                     break;
13004                 }
13005                 case 'square': {
13006                     $l = $size / 3;
13007                     $lspace += $l;
13008                     if ($this->rtl) {
13009                         $this->x += $lspace;
13010                     } else {
13011                         $this->x -= $lspace;
13012                     }
13013                     $this->Rect($this->x, ($this->y + (($this->lasth - $l)/ 2)), $l, $l, 'F', array(), $color);
13014                     break;
13015                 }
13016                 // ordered types
13017 
13018                 // $this->listcount[$this->listnum];
13019                 // $textitem
13020                 case '1':
13021                 case 'decimal': {
13022                     $textitem = $this->listcount[$this->listnum];
13023                     break;
13024                 }
13025                 case 'decimal-leading-zero': {
13026                     $textitem = sprintf("%02d", $this->listcount[$this->listnum]);
13027                     break;
13028                 }
13029                 case 'i':
13030                 case 'lower-roman': {
13031                     $textitem = strtolower($this->intToRoman($this->listcount[$this->listnum]));
13032                     break;
13033                 }
13034                 case 'I':
13035                 case 'upper-roman': {
13036                     $textitem = $this->intToRoman($this->listcount[$this->listnum]);
13037                     break;
13038                 }
13039                 case 'a':
13040                 case 'lower-alpha':
13041                 case 'lower-latin': {
13042                     $textitem = chr(97 + $this->listcount[$this->listnum] - 1);
13043                     break;
13044                 }
13045                 case 'A':
13046                 case 'upper-alpha':
13047                 case 'upper-latin': {
13048                     $textitem = chr(65 + $this->listcount[$this->listnum] - 1);
13049                     break;
13050                 }
13051                 case 'lower-greek': {
13052                     $textitem = $this->unichr(945 + $this->listcount[$this->listnum] - 1);
13053                     break;
13054                 }
13055                 /*
13056                 // Types to be implemented (special handling)
13057                 case 'hebrew': {
13058                     break;
13059                 }
13060                 case 'armenian': {
13061                     break;
13062                 }
13063                 case 'georgian': {
13064                     break;
13065                 }
13066                 case 'cjk-ideographic': {
13067                     break;
13068                 }
13069                 case 'hiragana': {
13070                     break;
13071                 }
13072                 case 'katakana': {
13073                     break;
13074                 }
13075                 case 'hiragana-iroha': {
13076                     break;
13077                 }
13078                 case 'katakana-iroha': {
13079                     break;
13080                 }
13081                 */
13082                 default: {
13083                     $textitem = $this->listcount[$this->listnum];
13084                 }
13085             }
13086             if (!$this->empty_string($textitem)) {
13087                 // print ordered item
13088                 if ($this->rtl) {
13089                     $textitem = '.'.$textitem;
13090                 } else {
13091                     $textitem = $textitem.'.';
13092                 }
13093                 $lspace += $this->GetStringWidth($textitem);
13094                 if ($this->rtl) {
13095                     $this->x += $lspace;
13096                 } else {
13097                     $this->x -= $lspace;
13098                 }
13099                 $this->Write($this->lasth, $textitem, '', false, '', false, 0, false);
13100             }
13101             $this->x = $tmpx;
13102             $this->lispacer = '';
13103         }
13104 
13111         protected function getGraphicVars() {
13112             $grapvars = array(
13113                 'FontFamily' => $this->FontFamily,
13114                 'FontStyle' => $this->FontStyle,
13115                 'FontSizePt' => $this->FontSizePt,
13116                 'rMargin' => $this->rMargin,
13117                 'lMargin' => $this->lMargin,
13118                 'cMargin' => $this->cMargin,
13119                 'LineWidth' => $this->LineWidth,
13120                 'linestyleWidth' => $this->linestyleWidth,
13121                 'linestyleCap' => $this->linestyleCap,
13122                 'linestyleJoin' => $this->linestyleJoin,
13123                 'linestyleDash' => $this->linestyleDash,
13124                 'DrawColor' => $this->DrawColor,
13125                 'FillColor' => $this->FillColor,
13126                 'TextColor' => $this->TextColor,
13127                 'ColorFlag' => $this->ColorFlag,
13128                 'bgcolor' => $this->bgcolor,
13129                 'fgcolor' => $this->fgcolor,
13130                 'htmlvspace' => $this->htmlvspace,
13131                 'lasth' => $this->lasth
13132                 );
13133             return $grapvars;
13134         }
13135 
13142         protected function setGraphicVars($gvars) {
13143             $this->FontFamily = $gvars['FontFamily'];
13144             $this->FontStyle = $gvars['FontStyle'];
13145             $this->FontSizePt = $gvars['FontSizePt'];
13146             $this->rMargin = $gvars['rMargin'];
13147             $this->lMargin = $gvars['lMargin'];
13148             $this->cMargin = $gvars['cMargin'];
13149             $this->LineWidth = $gvars['LineWidth'];
13150             $this->linestyleWidth = $gvars['linestyleWidth'];
13151             $this->linestyleCap = $gvars['linestyleCap'];
13152             $this->linestyleJoin = $gvars['linestyleJoin'];
13153             $this->linestyleDash = $gvars['linestyleDash'];
13154             $this->DrawColor = $gvars['DrawColor'];
13155             $this->FillColor = $gvars['FillColor'];
13156             $this->TextColor = $gvars['TextColor'];
13157             $this->ColorFlag = $gvars['ColorFlag'];
13158             $this->bgcolor = $gvars['bgcolor'];
13159             $this->fgcolor = $gvars['fgcolor'];
13160             $this->htmlvspace = $gvars['htmlvspace'];
13161             //$this->lasth = $gvars['lasth'];
13162             $this->_out(''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor.'');
13163             if (!$this->empty_string($this->FontFamily)) {
13164                 $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
13165             }
13166         }
13167 
13175         protected function getObjFilename($name) {
13176             return tempnam(K_PATH_CACHE, $name.'_');
13177         }
13178 
13187         protected function writeDiskCache($filename, $data, $append=false) {
13188             if ($append) {
13189                 $fmode = 'ab+';
13190             } else {
13191                 $fmode = 'wb+';
13192             }
13193             $f = @fopen($filename, $fmode);
13194             if (!$f) {
13195                 $this->Error('Unable to write cache file: '.$filename);
13196             } else {
13197                 fwrite($f, $data);
13198                 fclose($f);
13199             }
13200             // update file lenght (needed for transactions)
13201             if (!isset($this->cache_file_lenght['_'.$filename])) {
13202                 $this->cache_file_lenght['_'.$filename] = strlen($data);
13203             } else {
13204                 $this->cache_file_lenght['_'.$filename] += strlen($data);
13205             }
13206         }
13207 
13215         protected function readDiskCache($filename) {
13216             return file_get_contents($filename);
13217         }
13218 
13225         protected function setBuffer($data) {
13226             $this->bufferlen += strlen($data);
13227             if ($this->diskcache) {
13228                 if (!isset($this->buffer) OR $this->empty_string($this->buffer)) {
13229                     $this->buffer = $this->getObjFilename('buffer');
13230                 }
13231                 $this->writeDiskCache($this->buffer, $data, true);
13232             } else {
13233                 $this->buffer .= $data;
13234             }
13235         }
13236 
13243         protected function getBuffer() {
13244             if ($this->diskcache) {
13245                 return $this->readDiskCache($this->buffer);
13246             } else {
13247                 return $this->buffer;
13248             }
13249         }
13250 
13259         protected function setPageBuffer($page, $data, $append=false) {
13260             if ($this->diskcache) {
13261                 if (!isset($this->pages[$page])) {
13262                     $this->pages[$page] = $this->getObjFilename('page'.$page);
13263                 }
13264                 $this->writeDiskCache($this->pages[$page], $data, $append);
13265             } else {
13266                 if ($append) {
13267                     $this->pages[$page] .= $data;
13268                 } else {
13269                     $this->pages[$page] = $data;
13270                 }
13271             }
13272             if ($append AND isset($this->pagelen[$page])) {
13273                 $this->pagelen[$page] += strlen($data);
13274             } else {
13275                 $this->pagelen[$page] = strlen($data);
13276             }
13277         }
13278 
13286         protected function getPageBuffer($page) {
13287             if ($this->diskcache) {
13288                 return $this->readDiskCache($this->pages[$page]);
13289             } elseif (isset($this->pages[$page])) {
13290                 return $this->pages[$page];
13291             }
13292             return false;
13293         }
13294 
13302         protected function setImageBuffer($image, $data) {
13303             if ($this->diskcache) {
13304                 if (!isset($this->images[$image])) {
13305                     $this->images[$image] = $this->getObjFilename('image'.$image);
13306                 }
13307                 $this->writeDiskCache($this->images[$image], serialize($data));
13308             } else {
13309                 $this->images[$image] = $data;
13310             }
13311             if (!in_array($image, $this->imagekeys)) {
13312                 $this->imagekeys[] = $image;
13313             }
13314             ++$this->numimages;
13315         }
13316 
13325         protected function setImageSubBuffer($image, $key, $data) {
13326             if (!isset($this->images[$image])) {
13327                 $this->setImageBuffer($image, array());
13328             }
13329             if ($this->diskcache) {
13330                 $tmpimg = $this->getImageBuffer($image);
13331                 $tmpimg[$key] = $data;
13332                 $this->writeDiskCache($this->images[$image], serialize($tmpimg));
13333             } else {
13334                 $this->images[$image][$key] = $data;
13335             }
13336         }
13337 
13345         protected function getImageBuffer($image) {
13346             if ($this->diskcache AND isset($this->images[$image])) {
13347                 return unserialize($this->readDiskCache($this->images[$image]));
13348             } elseif (isset($this->images[$image])) {
13349                 return $this->images[$image];
13350             }
13351             return false;
13352         }
13353 
13361         protected function setFontBuffer($font, $data) {
13362             if ($this->diskcache) {
13363                 if (!isset($this->fonts[$font])) {
13364                     $this->fonts[$font] = $this->getObjFilename('font');
13365                 }
13366                 $this->writeDiskCache($this->fonts[$font], serialize($data));
13367             } else {
13368                 $this->fonts[$font] = $data;
13369             }
13370             if (!in_array($font, $this->fontkeys)) {
13371                 $this->fontkeys[] = $font;
13372             }
13373         }
13374 
13383         protected function setFontSubBuffer($font, $key, $data) {
13384             if (!isset($this->fonts[$font])) {
13385                 $this->setFontBuffer($font, array());
13386             }
13387             if ($this->diskcache) {
13388                 $tmpfont = $this->getFontBuffer($font);
13389                 $tmpfont[$key] = $data;
13390                 $this->writeDiskCache($this->fonts[$font], serialize($tmpfont));
13391             } else {
13392                 $this->fonts[$font][$key] = $data;
13393             }
13394         }
13395 
13403         protected function getFontBuffer($font) {
13404             if ($this->diskcache AND isset($this->fonts[$font])) {
13405                 return unserialize($this->readDiskCache($this->fonts[$font]));
13406             } elseif (isset($this->fonts[$font])) {
13407                 return $this->fonts[$font];
13408             }
13409             return false;
13410         }
13411 
13420         public function movePage($frompage, $topage) {
13421             if (($frompage > $this->numpages) OR ($frompage <= $topage)) {
13422                 return false;
13423             }
13424             if ($frompage == $this->page) {
13425                 // close the page before moving it
13426                 $this->endPage();
13427             }
13428             // move all page-related states
13429             $tmppage = $this->pages[$frompage];
13430             $tmppagedim = $this->pagedim[$frompage];
13431             $tmppagelen = $this->pagelen[$frompage];
13432             $tmpintmrk = $this->intmrk[$frompage];
13433             if (isset($this->footerpos[$frompage])) {
13434                 $tmpfooterpos = $this->footerpos[$frompage];
13435             }
13436             if (isset($this->footerlen[$frompage])) {
13437                 $tmpfooterlen = $this->footerlen[$frompage];
13438             }
13439             if (isset($this->transfmrk[$frompage])) {
13440                 $tmptransfmrk = $this->transfmrk[$frompage];
13441             }
13442             if (isset($this->PageAnnots[$frompage])) {
13443                 $tmpannots = $this->PageAnnots[$frompage];
13444             }
13445             if (isset($this->newpagegroup[$frompage])) {
13446                 $tmpnewpagegroup = $this->newpagegroup[$frompage];
13447             }
13448             for ($i = $frompage; $i > $topage; --$i) {
13449                 $j = $i - 1;
13450                 // shift pages down
13451                 $this->pages[$i] = $this->pages[$j];
13452                 $this->pagedim[$i] = $this->pagedim[$j];
13453                 $this->pagelen[$i] = $this->pagelen[$j];
13454                 $this->intmrk[$i] = $this->intmrk[$j];
13455                 if (isset($this->footerpos[$j])) {
13456                     $this->footerpos[$i] = $this->footerpos[$j];
13457                 } elseif (isset($this->footerpos[$i])) {
13458                     unset($this->footerpos[$i]);
13459                 }
13460                 if (isset($this->footerlen[$j])) {
13461                     $this->footerlen[$i] = $this->footerlen[$j];
13462                 } elseif (isset($this->footerlen[$i])) {
13463                     unset($this->footerlen[$i]);
13464                 }
13465                 if (isset($this->transfmrk[$j])) {
13466                     $this->transfmrk[$i] = $this->transfmrk[$j];
13467                 } elseif (isset($this->transfmrk[$i])) {
13468                     unset($this->transfmrk[$i]);
13469                 }
13470                 if (isset($this->PageAnnots[$j])) {
13471                     $this->PageAnnots[$i] = $this->PageAnnots[$j];
13472                 } elseif (isset($this->PageAnnots[$i])) {
13473                     unset($this->PageAnnots[$i]);
13474                 }
13475                 if (isset($this->newpagegroup[$j])) {
13476                     $this->newpagegroup[$i] = $this->newpagegroup[$j];
13477                 } elseif (isset($this->newpagegroup[$i])) {
13478                     unset($this->newpagegroup[$i]);
13479                 }
13480             }
13481             $this->pages[$topage] = $tmppage;
13482             $this->pagedim[$topage] = $tmppagedim;
13483             $this->pagelen[$topage] = $tmppagelen;
13484             $this->intmrk[$topage] = $tmpintmrk;
13485             if (isset($tmpfooterpos)) {
13486                 $this->footerpos[$topage] = $tmpfooterpos;
13487             } elseif (isset($this->footerpos[$topage])) {
13488                 unset($this->footerpos[$topage]);
13489             }
13490             if (isset($tmpfooterlen)) {
13491                 $this->footerlen[$topage] = $tmpfooterlen;
13492             } elseif (isset($this->footerlen[$topage])) {
13493                 unset($this->footerlen[$topage]);
13494             }
13495             if (isset($tmptransfmrk)) {
13496                 $this->transfmrk[$topage] = $tmptransfmrk;
13497             } elseif (isset($this->transfmrk[$topage])) {
13498                 unset($this->transfmrk[$topage]);
13499             }
13500             if (isset($tmpannots)) {
13501                 $this->PageAnnots[$topage] = $tmpannots;
13502             } elseif (isset($this->PageAnnots[$topage])) {
13503                 unset($this->PageAnnots[$topage]);
13504             }
13505             if (isset($tmpnewpagegroup)) {
13506                 $this->newpagegroup[$topage] = $tmpnewpagegroup;
13507             } elseif (isset($this->newpagegroup[$topage])) {
13508                 unset($this->newpagegroup[$topage]);
13509             }
13510             // adjust outlines
13511             $tmpoutlines = $this->outlines;
13512             foreach ($tmpoutlines as $key => $outline) {
13513                 if (($outline['p'] >= $topage) AND ($outline['p'] < $frompage)) {
13514                     $this->outlines[$key]['p'] = $outline['p'] + 1;
13515                 } elseif ($outline['p'] == $frompage) {
13516                     $this->outlines[$key]['p'] = $topage;
13517                 }
13518             }
13519             // adjust links
13520             $tmplinks = $this->links;
13521             foreach ($tmplinks as $key => $link) {
13522                 if (($link[0] >= $topage) AND ($link[0] < $frompage)) {
13523                     $this->links[$key][0] = $link[0] + 1;
13524                 } elseif ($link[0] == $frompage) {
13525                     $this->links[$key][0] = $topage;
13526                 }
13527             }
13528             // adjust javascript
13529             $tmpjavascript = $this->javascript;
13530             global $jfrompage, $jtopage;
13531             $jfrompage = $frompage;
13532             $jtopage = $topage;
13533             $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/',
13534                 create_function('$matches', 'global $jfrompage, $jtopage;
13535                 $pagenum = intval($matches[3]) + 1;
13536                 if (($pagenum >= $jtopage) AND ($pagenum < $jfrompage)) {
13537                     $newpage = ($pagenum + 1);
13538                 } elseif ($pagenum == $jfrompage) {
13539                     $newpage = $jtopage;
13540                 } else {
13541                     $newpage = $pagenum;
13542                 }
13543                 --$newpage;
13544                 return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript);
13545             // return to last page
13546             $this->lastPage(true);
13547             return true;
13548         }
13549 
13557         public function deletePage($page) {
13558             if ($page > $this->numpages) {
13559                 return false;
13560             }
13561             // delete current page
13562             unset($this->pages[$page]);
13563             unset($this->pagedim[$page]);
13564             unset($this->pagelen[$page]);
13565             unset($this->intmrk[$page]);
13566             if (isset($this->footerpos[$page])) {
13567                 unset($this->footerpos[$page]);
13568             }
13569             if (isset($this->footerlen[$page])) {
13570                 unset($this->footerlen[$page]);
13571             }
13572             if (isset($this->transfmrk[$page])) {
13573                 unset($this->transfmrk[$page]);
13574             }
13575             if (isset($this->PageAnnots[$page])) {
13576                 unset($this->PageAnnots[$page]);
13577             }
13578             if (isset($this->newpagegroup[$page])) {
13579                 unset($this->newpagegroup[$page]);
13580             }
13581             if (isset($this->pageopen[$page])) {
13582                 unset($this->pageopen[$page]);
13583             }
13584             // update remaining pages
13585             for ($i = $page; $i < $this->numpages; ++$i) {
13586                 $j = $i + 1;
13587                 // shift pages
13588                 $this->pages[$i] = $this->pages[$j];
13589                 $this->pagedim[$i] = $this->pagedim[$j];
13590                 $this->pagelen[$i] = $this->pagelen[$j];
13591                 $this->intmrk[$i] = $this->intmrk[$j];
13592                 if (isset($this->footerpos[$j])) {
13593                     $this->footerpos[$i] = $this->footerpos[$j];
13594                 } elseif (isset($this->footerpos[$i])) {
13595                     unset($this->footerpos[$i]);
13596                 }
13597                 if (isset($this->footerlen[$j])) {
13598                     $this->footerlen[$i] = $this->footerlen[$j];
13599                 } elseif (isset($this->footerlen[$i])) {
13600                     unset($this->footerlen[$i]);
13601                 }
13602                 if (isset($this->transfmrk[$j])) {
13603                     $this->transfmrk[$i] = $this->transfmrk[$j];
13604                 } elseif (isset($this->transfmrk[$i])) {
13605                     unset($this->transfmrk[$i]);
13606                 }
13607                 if (isset($this->PageAnnots[$j])) {
13608                     $this->PageAnnots[$i] = $this->PageAnnots[$j];
13609                 } elseif (isset($this->PageAnnots[$i])) {
13610                     unset($this->PageAnnots[$i]);
13611                 }
13612                 if (isset($this->newpagegroup[$j])) {
13613                     $this->newpagegroup[$i] = $this->newpagegroup[$j];
13614                 } elseif (isset($this->newpagegroup[$i])) {
13615                     unset($this->newpagegroup[$i]);
13616                 }
13617                 if (isset($this->pageopen[$j])) {
13618                     $this->pageopen[$i] = $this->pageopen[$j];
13619                 } elseif (isset($this->pageopen[$i])) {
13620                     unset($this->pageopen[$i]);
13621                 }
13622             }
13623             // remove last page
13624             unset($this->pages[$this->numpages]);
13625             unset($this->pagedim[$this->numpages]);
13626             unset($this->pagelen[$this->numpages]);
13627             unset($this->intmrk[$this->numpages]);
13628             if (isset($this->footerpos[$this->numpages])) {
13629                 unset($this->footerpos[$this->numpages]);
13630             }
13631             if (isset($this->footerlen[$this->numpages])) {
13632                 unset($this->footerlen[$this->numpages]);
13633             }
13634             if (isset($this->transfmrk[$this->numpages])) {
13635                 unset($this->transfmrk[$this->numpages]);
13636             }
13637             if (isset($this->PageAnnots[$this->numpages])) {
13638                 unset($this->PageAnnots[$this->numpages]);
13639             }
13640             if (isset($this->newpagegroup[$this->numpages])) {
13641                 unset($this->newpagegroup[$this->numpages]);
13642             }
13643             if (isset($this->pageopen[$this->numpages])) {
13644                 unset($this->pageopen[$this->numpages]);
13645             }
13646             --$this->numpages;
13647             $this->page = $this->numpages;
13648             // adjust outlines
13649             $tmpoutlines = $this->outlines;
13650             foreach ($tmpoutlines as $key => $outline) {
13651                 if ($outline['p'] > $page) {
13652                     $this->outlines[$key]['p'] = $outline['p'] - 1;
13653                 } elseif ($outline['p'] == $page) {
13654                     unset($this->outlines[$key]);
13655                 }
13656             }
13657             // adjust links
13658             $tmplinks = $this->links;
13659             foreach ($tmplinks as $key => $link) {
13660                 if ($link[0] > $page) {
13661                     $this->links[$key][0] = $link[0] - 1;
13662                 } elseif ($link[0] == $page) {
13663                     unset($this->links[$key]);
13664                 }
13665             }
13666             // adjust javascript
13667             $tmpjavascript = $this->javascript;
13668             global $jpage;
13669             $jpage = $page;
13670             $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/',
13671                 create_function('$matches', 'global $jpage;
13672                 $pagenum = intval($matches[3]) + 1;
13673                 if ($pagenum >= $jpage) {
13674                     $newpage = ($pagenum - 1);
13675                 } elseif ($pagenum == $jpage) {
13676                     $newpage = 1;
13677                 } else {
13678                     $newpage = $pagenum;
13679                 }
13680                 --$newpage;
13681                 return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript);
13682             // return to last page
13683             $this->lastPage(true);
13684             return true;
13685         }
13686 
13697         public function addTOC($page='', $numbersfont='', $filler='.') {
13698             $fontsize = $this->FontSizePt;
13699             $fontfamily = $this->FontFamily;
13700             $fontstyle = $this->FontStyle;
13701             $w = $this->w - $this->lMargin - $this->rMargin;
13702             $spacer = $this->GetStringWidth(' ') * 4;
13703             $page_first = $this->getPage();
13704             $lmargin = $this->lMargin;
13705             $rmargin = $this->rMargin;
13706             $x_start = $this->GetX();
13707             if ($this->empty_string($numbersfont)) {
13708                 $numbersfont = $this->default_monospaced_font;
13709             }
13710             if ($this->empty_string($filler)) {
13711                 $filler = ' ';
13712             }
13713             if ($this->empty_string($page)) {
13714                 $gap = ' ';
13715             } else {
13716                 $gap = '';
13717             }
13718             foreach ($this->outlines as $key => $outline) {
13719                 if ($this->rtl) {
13720                     $aligntext = 'R';
13721                     $alignnum = 'L';
13722                 } else {
13723                     $aligntext = 'L';
13724                     $alignnum = 'R';
13725                 }
13726                 if ($outline['l'] == 0) {
13727                     $this->SetFont($fontfamily, $fontstyle.'B', $fontsize);
13728                 } else {
13729                     $this->SetFont($fontfamily, $fontstyle, $fontsize - $outline['l']);
13730                 }
13731                 $indent = ($spacer * $outline['l']);
13732                 if ($this->rtl) {
13733                     $this->rMargin += $indent;
13734                     $this->x -= $indent;
13735                 } else {
13736                     $this->lMargin += $indent;
13737                     $this->x += $indent;
13738                 }
13739                 $link = $this->AddLink();
13740                 $this->SetLink($link, 0, $outline['p']);
13741                 // write the text
13742                 $this->Write(0, $outline['t'], $link, 0, $aligntext, false, 0, false, false, 0);
13743                 $this->SetFont($numbersfont, $fontstyle, $fontsize);
13744                 if ($this->empty_string($page)) {
13745                     $pagenum = $outline['p'];
13746                 } else {
13747                     // placemark to be replaced with the correct number
13748                     $pagenum = '{#'.($outline['p']).'}';
13749                     if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
13750                         $pagenum = '{'.$pagenum.'}';
13751                     }
13752                 }
13753                 $numwidth = $this->GetStringWidth($pagenum);
13754                 if ($this->rtl) {
13755                     $tw = $this->x - $this->lMargin;
13756                 } else {
13757                     $tw = $this->w - $this->rMargin - $this->x;
13758                 }
13759                 $fw = $tw - $numwidth - $this->GetStringWidth(' ');
13760                 $numfills = floor($fw / $this->GetStringWidth($filler));
13761                 if ($numfills > 0) {
13762                     $rowfill = str_repeat($filler, $numfills);
13763                 } else {
13764                     $rowfill = '';
13765                 }
13766                 if ($this->rtl) {
13767                     $pagenum = $pagenum.$gap.$rowfill.' ';
13768                 } else {
13769                     $pagenum = ' '.$rowfill.$gap.$pagenum;
13770                 }
13771                 // write the number
13772                 //$this->SetX($x_start);
13773                 $this->Cell($tw, 0, $pagenum, 0, 1, $alignnum, 0, $link, 0);
13774                 $this->SetX($x_start);
13775                 $this->lMargin = $lmargin;
13776                 $this->rMargin = $rmargin;
13777             }
13778             $page_last = $this->getPage();
13779             $numpages = $page_last - $page_first + 1;
13780             if (!$this->empty_string($page)) {
13781                 for($p = $page_first; $p <= $page_last; ++$p) {
13782                     // get page data
13783                     $temppage = $this->getPageBuffer($p);
13784                     for($n = 1; $n <= $this->numpages; ++$n) {
13785                         // update page numbers
13786                         $k = '{#'.$n.'}';
13787                         $ku = '{'.$k.'}';
13788                         $alias_a = $this->_escape($k);
13789                         $alias_au = $this->_escape('{'.$k.'}');
13790                         if ($this->isunicode) {
13791                             $alias_b = $this->_escape($this->UTF8ToLatin1($k));
13792                             $alias_bu = $this->_escape($this->UTF8ToLatin1($ku));
13793                             $alias_c = $this->_escape($this->utf8StrRev($k, false, $this->tmprtl));
13794                             $alias_cu = $this->_escape($this->utf8StrRev($ku, false, $this->tmprtl));
13795                         }
13796                         if ($n >= $page) {
13797                             $np = $n + $numpages;
13798                         } else {
13799                             $np = $n;
13800                         }
13801                         $ns = $this->formatTOCPageNumber($np);
13802                         $nu = $ns;
13803                         $sdiff = strlen($k) - strlen($ns) - 1;
13804                         $sdiffu = strlen($ku) - strlen($ns) - 1;
13805                         $sfill = str_repeat($filler, $sdiff);
13806                         $sfillu = str_repeat($filler, $sdiffu);
13807                         if ($this->rtl) {
13808                             $ns = $ns.' '.$sfill;
13809                             $nu = $nu.' '.$sfillu;
13810                         } else {
13811                             $ns = $sfill.' '.$ns;
13812                             $nu = $sfillu.' '.$nu;
13813                         }
13814                         $nu = $this->UTF8ToUTF16BE($nu, false);
13815                         $temppage = str_replace($alias_au, $nu, $temppage);
13816                         if ($this->isunicode) {
13817                             $temppage = str_replace($alias_bu, $nu, $temppage);
13818                             $temppage = str_replace($alias_cu, $nu, $temppage);
13819                             $temppage = str_replace($alias_b, $ns, $temppage);
13820                             $temppage = str_replace($alias_c, $ns, $temppage);
13821                         }
13822                         $temppage = str_replace($alias_a, $ns, $temppage);
13823                     }
13824                     // save changes
13825                     $this->setPageBuffer($p, $temppage);
13826                 }
13827                 // move pages
13828                 for ($i = 0; $i < $numpages; ++$i) {
13829                     $this->movePage($page_last, $page);
13830                 }
13831             }
13832             $this->SetFont($fontfamily, $fontstyle, $fontsize);
13833         }
13834 
13840         public function startTransaction() {
13841             if (isset($this->objcopy)) {
13842                 // remove previous copy
13843                 $this->commitTransaction();
13844             }
13845             // clone current object
13846             $this->objcopy = $this->objclone($this);
13847         }
13848 
13854         public function commitTransaction() {
13855             if (isset($this->objcopy)) {
13856                 $this->objcopy->_destroy(true, true);
13857                 unset($this->objcopy);
13858             }
13859         }
13860 
13867         public function rollbackTransaction() {
13868             if (isset($this->objcopy)) {
13869                 if (isset($this->objcopy->diskcache) AND $this->objcopy->diskcache) {
13870                     // truncate files to previous values
13871                     foreach ($this->objcopy->cache_file_lenght as $file => $lenght) {
13872                         $file = substr($file, 1);
13873                         $handle = fopen($file, 'r+');
13874                         ftruncate($handle, $lenght);
13875                     }
13876                 }
13877                 $this->_destroy(true, true);
13878                 return $this->objcopy;
13879             }
13880             return $this;
13881         }
13882 
13890         public function objclone($object) {
13891             return @clone($object);
13892         }
13893 
13901         public function empty_string($str) {
13902             return (is_null($str) OR (is_string($str) AND (strlen($str) == 0)));
13903         }
13904         
13905     } // END OF TCPDF CLASS
13906 }
13907 //============================================================+
13908 // END OF FILE
13909 //============================================================+
13910 ?>

Generated on Tue Sep 29 16:45:15 2009 for OXID eShop CE by  doxygen 1.5.5