|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00002 //============================================================+ 00003 // File name : tcpdf.php 00004 // Version : 5.9.133 00005 // Begin : 2002-08-03 00006 // Last Update : 2011-10-26 00007 // Author : Nicola Asuni - Tecnick.com S.r.l - Via Della Pace, 11 - 09044 - Quartucciu (CA) - ITALY - www.tecnick.com - info@tecnick.com 00008 // License : http://www.tecnick.com/pagefiles/tcpdf/LICENSE.TXT GNU-LGPLv3 + YOU CAN'T REMOVE ANY TCPDF COPYRIGHT NOTICE OR LINK FROM THE GENERATED PDF DOCUMENTS. 00009 // ------------------------------------------------------------------- 00010 // 00011 // This file is part of TCPDF software library. 00012 // 00013 // TCPDF is free software: you can redistribute it and/or modify it 00014 // under the terms of the GNU Lesser General Public License as 00015 // published by the Free Software Foundation, either version 3 of the 00016 // License, or (at your option) any later version. Additionally, 00017 // YOU CAN'T REMOVE ANY TCPDF COPYRIGHT NOTICE OR LINK FROM THE 00018 // GENERATED PDF DOCUMENTS. 00019 // 00020 // TCPDF is distributed in the hope that it will be useful, but 00021 // WITHOUT ANY WARRANTY; without even the implied warranty of 00022 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 00023 // See the GNU Lesser General Public License for more details. 00024 // 00025 // You should have received a copy of the License 00026 // along with TCPDF. If not, see 00027 // <http://www.tecnick.com/pagefiles/tcpdf/LICENSE.TXT>. 00028 // 00029 // See LICENSE.TXT file for more information. 00030 // ------------------------------------------------------------------- 00031 // 00032 // Description : 00033 // This is a PHP class for generating PDF documents without requiring external extensions. 00034 // 00035 // NOTE: 00036 // This class was originally derived in 2002 from the Public 00037 // Domain FPDF class by Olivier Plathey (http://www.fpdf.org), 00038 // but now is almost entirely rewritten and contains thousands of 00039 // new lines of code and hundreds new features. 00040 // 00041 // Main features: 00042 // * no external libraries are required for the basic functions; 00043 // * all standard page formats, custom page formats, custom margins and units of measure; 00044 // * UTF-8 Unicode and Right-To-Left languages; 00045 // * TrueTypeUnicode, TrueType, Type1 and CID-0 fonts; 00046 // * font subsetting; 00047 // * methods to publish some XHTML + CSS code, Javascript and Forms; 00048 // * images, graphic (geometric figures) and transformation methods; 00049 // * supports JPEG, PNG and SVG images natively, all images supported by GD (GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM) and all images supported via ImagMagick (http://www.imagemagick.org/www/formats.html) 00050 // * 1D and 2D barcodes: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extention, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS, Datamatrix, QR-Code, PDF417; 00051 // * JPEG and PNG ICC profiles, Grayscale, RGB, CMYK, Spot Colors and Transparencies; 00052 // * automatic page header and footer management; 00053 // * document encryption up to 256 bit and digital signature certifications; 00054 // * transactions to UNDO commands; 00055 // * PDF annotations, including links, text and file attachments; 00056 // * text rendering modes (fill, stroke and clipping); 00057 // * multiple columns mode; 00058 // * no-write page regions; 00059 // * bookmarks, named destinations and table of content; 00060 // * text hyphenation; 00061 // * text stretching and spacing (tracking/kerning); 00062 // * automatic page break, line break and text alignments including justification; 00063 // * automatic page numbering and page groups; 00064 // * move and delete pages; 00065 // * page compression (requires php-zlib extension); 00066 // * XOBject Templates; 00067 // * Layers and object visibility. 00068 // * PDF/A-1b support. 00069 // 00070 // ----------------------------------------------------------- 00071 // THANKS TO: 00072 // 00073 // Olivier Plathey (http://www.fpdf.org) for original FPDF. 00074 // Efthimios Mavrogeorgiadis (emavro@yahoo.com) for suggestions on RTL language support. 00075 // Klemen Vodopivec (http://www.fpdf.de/downloads/addons/37/) for Encryption algorithm. 00076 // Warren Sherliker (wsherliker@gmail.com) for better image handling. 00077 // dullus for text Justification. 00078 // Bob Vincent (pillarsdotnet@users.sourceforge.net) for <li> value attribute. 00079 // Patrick Benny for text stretch suggestion on Cell(). 00080 // Johannes Güntert for JavaScript support. 00081 // Denis Van Nuffelen for Dynamic Form. 00082 // Jacek Czekaj for multibyte justification 00083 // Anthony Ferrara for the reintroduction of legacy image methods. 00084 // Sourceforge user 1707880 (hucste) for line-trough mode. 00085 // Larry Stanbery for page groups. 00086 // Martin Hall-May for transparency. 00087 // Aaron C. Spike for Polycurve method. 00088 // Mohamad Ali Golkar, Saleh AlMatrafe, Charles Abbott for Arabic and Persian support. 00089 // Moritz Wagner and Andreas Wurmser for graphic functions. 00090 // Andrew Whitehead for core fonts support. 00091 // Esteban Joël Marín for OpenType font conversion. 00092 // Teus Hagen for several suggestions and fixes. 00093 // Yukihiro Nakadaira for CID-0 CJK fonts fixes. 00094 // Kosmas Papachristos for some CSS improvements. 00095 // Marcel Partap for some fixes. 00096 // Won Kyu Park for several suggestions, fixes and patches. 00097 // Dominik Dzienia for QR-code support. 00098 // Laurent Minguet for some suggestions. 00099 // Christian Deligant for some suggestions and fixes. 00100 // Anyone that has reported a bug or sent a suggestion. 00101 //============================================================+ 00102 00143 // Main configuration file. Define the K_TCPDF_EXTERNAL_CONFIG constant to skip this file. 00144 require_once(dirname(__FILE__).'/config/tcpdf_config.php'); 00145 00155 class TCPDF { 00156 00157 // private properties 00158 00163 private $tcpdf_version = '5.9.133'; 00164 00165 // Protected properties 00166 00171 protected $page; 00172 00177 protected $n; 00178 00183 protected $offsets; 00184 00189 protected $buffer; 00190 00195 protected $pages = array(); 00196 00201 protected $state; 00202 00207 protected $compress; 00208 00213 protected $CurOrientation; 00214 00219 protected $pagedim = array(); 00220 00225 protected $k; 00226 00231 protected $fwPt; 00232 00237 protected $fhPt; 00238 00243 protected $wPt; 00244 00249 protected $hPt; 00250 00255 protected $w; 00256 00261 protected $h; 00262 00267 protected $lMargin; 00268 00273 protected $tMargin; 00274 00279 protected $rMargin; 00280 00285 protected $bMargin; 00286 00292 protected $cell_padding = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0); 00293 00299 protected $cell_margin = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0); 00300 00305 protected $x; 00306 00311 protected $y; 00312 00317 protected $lasth; 00318 00323 protected $LineWidth; 00324 00329 protected $CoreFonts; 00330 00335 protected $fonts = array(); 00336 00341 protected $FontFiles = array(); 00342 00347 protected $diffs = array(); 00348 00353 protected $images = array(); 00354 00359 protected $PageAnnots = array(); 00360 00365 protected $links = array(); 00366 00371 protected $FontFamily; 00372 00377 protected $FontStyle; 00378 00384 protected $FontAscent; 00385 00391 protected $FontDescent; 00392 00397 protected $underline; 00398 00403 protected $overline; 00404 00409 protected $CurrentFont; 00410 00415 protected $FontSizePt; 00416 00421 protected $FontSize; 00422 00427 protected $DrawColor; 00428 00433 protected $FillColor; 00434 00439 protected $TextColor; 00440 00445 protected $ColorFlag; 00446 00451 protected $AutoPageBreak; 00452 00457 protected $PageBreakTrigger; 00458 00463 protected $InHeader = false; 00464 00469 protected $InFooter = false; 00470 00475 protected $ZoomMode; 00476 00481 protected $LayoutMode; 00482 00487 protected $docinfounicode = true; 00488 00493 protected $title = ''; 00494 00499 protected $subject = ''; 00500 00505 protected $author = ''; 00506 00511 protected $keywords = ''; 00512 00517 protected $creator = ''; 00518 00523 protected $starting_page_number = 1; 00524 00529 protected $alias_tot_pages = '{:ptp:}'; 00530 00535 protected $alias_num_page = '{:pnp:}'; 00536 00541 protected $alias_group_tot_pages = '{:ptg:}'; 00542 00547 protected $alias_group_num_page = '{:png:}'; 00548 00553 protected $alias_right_shift = '{rsc:'; 00554 00561 protected $img_rb_x; 00562 00569 protected $img_rb_y; 00570 00577 protected $imgscale = 1; 00578 00585 protected $isunicode = false; 00586 00593 protected $unicode; 00594 00601 protected $encmaps; 00602 00608 protected $PDFVersion = '1.7'; 00609 00614 protected $header_xobjid = -1; 00615 00620 protected $header_xobj_autoreset = false; 00621 00626 protected $header_margin; 00627 00632 protected $footer_margin; 00633 00639 protected $original_lMargin; 00640 00646 protected $original_rMargin; 00647 00652 protected $header_font; 00653 00658 protected $footer_font; 00659 00664 protected $l; 00665 00670 protected $barcode = false; 00671 00676 protected $print_header = true; 00677 00682 protected $print_footer = true; 00683 00688 protected $header_logo = ''; 00689 00694 protected $header_logo_width = 30; 00695 00700 protected $header_title = ''; 00701 00706 protected $header_string = ''; 00707 00712 protected $default_table_columns = 4; 00713 00714 // variables for html parser 00715 00720 protected $HREF = array(); 00721 00726 protected $fontlist = array(); 00727 00732 protected $fgcolor; 00733 00738 protected $listordered = array(); 00739 00744 protected $listcount = array(); 00745 00750 protected $listnum = 0; 00751 00756 protected $listindent = 0; 00757 00762 protected $listindentlevel = 0; 00763 00768 protected $bgcolor; 00769 00774 protected $tempfontsize = 10; 00775 00780 protected $lispacer = ''; 00781 00787 protected $encoding = 'UTF-8'; 00788 00794 protected $internal_encoding; 00795 00801 protected $rtl = false; 00802 00808 protected $tmprtl = false; 00809 00810 // --- Variables used for document encryption: 00811 00817 protected $encrypted; 00818 00824 protected $encryptdata = array(); 00825 00831 protected $last_enc_key; 00832 00838 protected $last_enc_key_c; 00839 00844 protected $enc_padding = "\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A"; 00845 00851 protected $file_id; 00852 00853 // --- bookmark --- 00854 00860 protected $outlines = array(); 00861 00867 protected $OutlineRoot; 00868 00869 // --- javascript and form --- 00870 00876 protected $javascript = ''; 00877 00883 protected $n_js; 00884 00890 protected $linethrough; 00891 00897 protected $ur = array(); 00898 00904 protected $dpi = 72; 00905 00911 protected $newpagegroup = array(); 00912 00918 protected $pagegroups = array(); 00919 00925 protected $currpagegroup = 0; 00926 00932 protected $extgstates; 00933 00939 protected $jpeg_quality; 00940 00946 protected $cell_height_ratio = K_CELL_HEIGHT_RATIO; 00947 00953 protected $viewer_preferences; 00954 00960 protected $PageMode; 00961 00967 protected $gradients = array(); 00968 00974 protected $intmrk = array(); 00975 00981 protected $bordermrk = array(); 00982 00988 protected $emptypagemrk = array(); 00989 00995 protected $cntmrk = array(); 00996 01002 protected $footerpos = array(); 01003 01009 protected $footerlen = array(); 01010 01016 protected $newline = true; 01017 01023 protected $endlinex = 0; 01024 01030 protected $linestyleWidth = ''; 01031 01037 protected $linestyleCap = '0 J'; 01038 01044 protected $linestyleJoin = '0 j'; 01045 01051 protected $linestyleDash = '[] 0 d'; 01052 01058 protected $openMarkedContent = false; 01059 01065 protected $htmlvspace = 0; 01066 01072 protected $spot_colors = array(); 01073 01079 protected $lisymbol = ''; 01080 01086 protected $epsmarker = 'x#!#EPS#!#x'; 01087 01093 protected $transfmatrix = array(); 01094 01100 protected $transfmatrix_key = 0; 01101 01107 protected $booklet = false; 01108 01114 protected $feps = 0.005; 01115 01121 protected $tagvspaces = array(); 01122 01128 protected $customlistindent = -1; 01129 01135 protected $opencell = true; 01136 01142 protected $embeddedfiles = array(); 01143 01149 protected $premode = false; 01150 01157 protected $transfmrk = array(); 01158 01164 protected $htmlLinkColorArray = array(0, 0, 255); 01165 01171 protected $htmlLinkFontStyle = 'U'; 01172 01178 protected $numpages = 0; 01179 01185 protected $pagelen = array(); 01186 01192 protected $numimages = 0; 01193 01199 protected $imagekeys = array(); 01200 01206 protected $bufferlen = 0; 01207 01213 protected $diskcache = false; 01214 01220 protected $numfonts = 0; 01221 01227 protected $fontkeys = array(); 01228 01234 protected $font_obj_ids = array(); 01235 01241 protected $pageopen = array(); 01242 01248 protected $default_monospaced_font = 'courier'; 01249 01255 protected $objcopy; 01256 01262 protected $cache_file_length = array(); 01263 01269 protected $thead = ''; 01270 01276 protected $theadMargins = array(); 01277 01283 protected $cache_UTF8StringToArray = array(); 01284 01290 protected $cache_maxsize_UTF8StringToArray = 8; 01291 01297 protected $cache_size_UTF8StringToArray = 0; 01298 01304 protected $sign = false; 01305 01311 protected $signature_data = array(); 01312 01318 protected $signature_max_length = 11742; 01319 01325 protected $signature_appearance = array('page' => 1, 'rect' => '0 0 0 0'); 01326 01332 protected $empty_signature_appearance = array(); 01333 01339 protected $re_spaces = '/[^\S\xa0]/'; 01340 01346 protected $re_space = array('p' => '[^\S\xa0]', 'm' => ''); 01347 01353 protected $sig_obj_id = 0; 01354 01360 protected $byterange_string = '/ByteRange[0 ********** ********** **********]'; 01361 01367 protected $sig_annot_ref = '***SIGANNREF*** 0 R'; 01368 01374 protected $page_obj_id = array(); 01375 01381 protected $form_obj_id = array(); 01382 01388 protected $default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128)); 01389 01395 protected $js_objects = array(); 01396 01402 protected $form_action = ''; 01403 01409 protected $form_enctype = 'application/x-www-form-urlencoded'; 01410 01416 protected $form_mode = 'post'; 01417 01423 protected $annotation_fonts = array(); 01424 01430 protected $radiobutton_groups = array(); 01431 01437 protected $radio_groups = array(); 01438 01444 protected $textindent = 0; 01445 01451 protected $start_transaction_page = 0; 01452 01458 protected $start_transaction_y = 0; 01459 01465 protected $inthead = false; 01466 01472 protected $columns = array(); 01473 01479 protected $num_columns = 1; 01480 01486 protected $current_column = 0; 01487 01493 protected $column_start_page = 0; 01494 01500 protected $maxselcol = array('page' => 0, 'column' => 0); 01501 01507 protected $colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0)); 01508 01514 protected $textrendermode = 0; 01515 01521 protected $textstrokewidth = 0; 01522 01528 protected $strokecolor; 01529 01535 protected $pdfunit = 'mm'; 01536 01541 protected $tocpage = false; 01542 01548 protected $rasterize_vector_images = false; 01549 01555 protected $font_subsetting = true; 01556 01562 protected $default_graphic_vars = array(); 01563 01569 protected $xobjects = array(); 01570 01576 protected $inxobj = false; 01577 01583 protected $xobjid = ''; 01584 01590 protected $font_stretching = 100; 01591 01597 protected $font_spacing = 0; 01598 01605 protected $page_regions = array(); 01606 01612 protected $webcolor = array(); 01613 01619 protected $spotcolor = array(); 01620 01626 protected $pdflayers = array(); 01627 01633 protected $dests = array(); 01634 01640 protected $n_dests; 01641 01647 protected $svgdir = ''; 01648 01654 protected $svgunit = 'px'; 01655 01661 protected $svggradients = array(); 01662 01668 protected $svggradientid = 0; 01669 01675 protected $svgdefsmode = false; 01676 01682 protected $svgdefs = array(); 01683 01689 protected $svgclipmode = false; 01690 01696 protected $svgclippaths = array(); 01697 01703 protected $svgcliptm = array(); 01704 01710 protected $svgclipid = 0; 01711 01717 protected $svgtext = ''; 01718 01724 protected $svgtextmode = array(); 01725 01731 protected $svginheritprop = array('clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cursor', 'direction', 'fill', 'fill-opacity', 'fill-rule', 'font', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'glyph-orientation-horizontal', 'glyph-orientation-vertical', 'image-rendering', 'kerning', 'letter-spacing', 'marker', 'marker-end', 'marker-mid', 'marker-start', 'pointer-events', 'shape-rendering', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'text-anchor', 'text-rendering', 'visibility', 'word-spacing', 'writing-mode'); 01732 01738 protected $svgstyles = array(array( 01739 'alignment-baseline' => 'auto', 01740 'baseline-shift' => 'baseline', 01741 'clip' => 'auto', 01742 'clip-path' => 'none', 01743 'clip-rule' => 'nonzero', 01744 'color' => 'black', 01745 'color-interpolation' => 'sRGB', 01746 'color-interpolation-filters' => 'linearRGB', 01747 'color-profile' => 'auto', 01748 'color-rendering' => 'auto', 01749 'cursor' => 'auto', 01750 'direction' => 'ltr', 01751 'display' => 'inline', 01752 'dominant-baseline' => 'auto', 01753 'enable-background' => 'accumulate', 01754 'fill' => 'black', 01755 'fill-opacity' => 1, 01756 'fill-rule' => 'nonzero', 01757 'filter' => 'none', 01758 'flood-color' => 'black', 01759 'flood-opacity' => 1, 01760 'font' => '', 01761 'font-family' => 'helvetica', 01762 'font-size' => 'medium', 01763 'font-size-adjust' => 'none', 01764 'font-stretch' => 'normal', 01765 'font-style' => 'normal', 01766 'font-variant' => 'normal', 01767 'font-weight' => 'normal', 01768 'glyph-orientation-horizontal' => '0deg', 01769 'glyph-orientation-vertical' => 'auto', 01770 'image-rendering' => 'auto', 01771 'kerning' => 'auto', 01772 'letter-spacing' => 'normal', 01773 'lighting-color' => 'white', 01774 'marker' => '', 01775 'marker-end' => 'none', 01776 'marker-mid' => 'none', 01777 'marker-start' => 'none', 01778 'mask' => 'none', 01779 'opacity' => 1, 01780 'overflow' => 'auto', 01781 'pointer-events' => 'visiblePainted', 01782 'shape-rendering' => 'auto', 01783 'stop-color' => 'black', 01784 'stop-opacity' => 1, 01785 'stroke' => 'none', 01786 'stroke-dasharray' => 'none', 01787 'stroke-dashoffset' => 0, 01788 'stroke-linecap' => 'butt', 01789 'stroke-linejoin' => 'miter', 01790 'stroke-miterlimit' => 4, 01791 'stroke-opacity' => 1, 01792 'stroke-width' => 1, 01793 'text-anchor' => 'start', 01794 'text-decoration' => 'none', 01795 'text-rendering' => 'auto', 01796 'unicode-bidi' => 'normal', 01797 'visibility' => 'visible', 01798 'word-spacing' => 'normal', 01799 'writing-mode' => 'lr-tb', 01800 'text-color' => 'black', 01801 'transfmatrix' => array(1, 0, 0, 1, 0, 0) 01802 )); 01803 01809 protected $force_srgb = false; 01810 01816 protected $pdfa_mode = false; 01817 01823 protected $doc_date; 01824 01830 protected $custom_xmp = ''; 01831 01832 //------------------------------------------------------------ 01833 // METHODS 01834 //------------------------------------------------------------ 01835 01849 public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false, $pdfa=false) { 01850 /* Set internal character encoding to ASCII */ 01851 if (function_exists('mb_internal_encoding') AND mb_internal_encoding()) { 01852 $this->internal_encoding = mb_internal_encoding(); 01853 mb_internal_encoding('ASCII'); 01854 } 01855 // get array of HTML colors 01856 require(dirname(__FILE__).'/htmlcolors.php'); 01857 $this->webcolor = $webcolor; 01858 // get array of custom spot colors 01859 if (file_exists(dirname(__FILE__).'/spotcolors.php')) { 01860 require(dirname(__FILE__).'/spotcolors.php'); 01861 $this->spotcolor = $spotcolor; 01862 } else { 01863 $this->spotcolor = array(); 01864 } 01865 require_once(dirname(__FILE__).'/unicode_data.php'); 01866 $this->unicode = new TCPDF_UNICODE_DATA(); 01867 require_once(dirname(__FILE__).'/encodings_maps.php'); 01868 $this->encmaps = new TCPDF_ENCODING_MAPS(); 01869 $this->font_obj_ids = array(); 01870 $this->page_obj_id = array(); 01871 $this->form_obj_id = array(); 01872 // set pdf/a mode 01873 $this->pdfa_mode = $pdfa; 01874 $this->force_srgb = false; 01875 // set disk caching 01876 $this->diskcache = $diskcache ? true : false; 01877 // set language direction 01878 $this->rtl = false; 01879 $this->tmprtl = false; 01880 // some checks 01881 $this->_dochecks(); 01882 // initialization of properties 01883 $this->isunicode = $unicode; 01884 $this->page = 0; 01885 $this->transfmrk[0] = array(); 01886 $this->pagedim = array(); 01887 $this->n = 2; 01888 $this->buffer = ''; 01889 $this->pages = array(); 01890 $this->state = 0; 01891 $this->fonts = array(); 01892 $this->FontFiles = array(); 01893 $this->diffs = array(); 01894 $this->images = array(); 01895 $this->links = array(); 01896 $this->gradients = array(); 01897 $this->InFooter = false; 01898 $this->lasth = 0; 01899 $this->FontFamily = 'helvetica'; 01900 $this->FontStyle = ''; 01901 $this->FontSizePt = 12; 01902 $this->underline = false; 01903 $this->overline = false; 01904 $this->linethrough = false; 01905 $this->DrawColor = '0 G'; 01906 $this->FillColor = '0 g'; 01907 $this->TextColor = '0 g'; 01908 $this->ColorFlag = false; 01909 $this->pdflayers = array(); 01910 // encryption values 01911 $this->encrypted = false; 01912 $this->last_enc_key = ''; 01913 // standard Unicode fonts 01914 $this->CoreFonts = array( 01915 'courier'=>'Courier', 01916 'courierB'=>'Courier-Bold', 01917 'courierI'=>'Courier-Oblique', 01918 'courierBI'=>'Courier-BoldOblique', 01919 'helvetica'=>'Helvetica', 01920 'helveticaB'=>'Helvetica-Bold', 01921 'helveticaI'=>'Helvetica-Oblique', 01922 'helveticaBI'=>'Helvetica-BoldOblique', 01923 'times'=>'Times-Roman', 01924 'timesB'=>'Times-Bold', 01925 'timesI'=>'Times-Italic', 01926 'timesBI'=>'Times-BoldItalic', 01927 'symbol'=>'Symbol', 01928 'zapfdingbats'=>'ZapfDingbats' 01929 ); 01930 // set scale factor 01931 $this->setPageUnit($unit); 01932 // set page format and orientation 01933 $this->setPageFormat($format, $orientation); 01934 // page margins (1 cm) 01935 $margin = 28.35 / $this->k; 01936 $this->SetMargins($margin, $margin); 01937 // internal cell padding 01938 $cpadding = $margin / 10; 01939 $this->setCellPaddings($cpadding, 0, $cpadding, 0); 01940 // cell margins 01941 $this->setCellMargins(0, 0, 0, 0); 01942 // line width (0.2 mm) 01943 $this->LineWidth = 0.57 / $this->k; 01944 $this->linestyleWidth = sprintf('%.2F w', ($this->LineWidth * $this->k)); 01945 $this->linestyleCap = '0 J'; 01946 $this->linestyleJoin = '0 j'; 01947 $this->linestyleDash = '[] 0 d'; 01948 // automatic page break 01949 $this->SetAutoPageBreak(true, (2 * $margin)); 01950 // full width display mode 01951 $this->SetDisplayMode('fullwidth'); 01952 // compression 01953 $this->SetCompression(); 01954 // set default PDF version number 01955 $this->setPDFVersion(); 01956 $this->encoding = $encoding; 01957 $this->HREF = array(); 01958 $this->getFontsList(); 01959 $this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0); 01960 $this->strokecolor = array('R' => 0, 'G' => 0, 'B' => 0); 01961 $this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255); 01962 $this->extgstates = array(); 01963 // user's rights 01964 $this->sign = false; 01965 $this->ur['enabled'] = false; 01966 $this->ur['document'] = '/FullSave'; 01967 $this->ur['annots'] = '/Create/Delete/Modify/Copy/Import/Export'; 01968 $this->ur['form'] = '/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate'; 01969 $this->ur['signature'] = '/Modify'; 01970 $this->ur['ef'] = '/Create/Delete/Modify/Import'; 01971 $this->ur['formex'] = ''; 01972 $this->signature_appearance = array('page' => 1, 'rect' => '0 0 0 0'); 01973 $this->empty_signature_appearance = array(); 01974 // set default JPEG quality 01975 $this->jpeg_quality = 75; 01976 // initialize some settings 01977 $this->utf8Bidi(array(''), ''); 01978 // set default font 01979 $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt); 01980 // check if PCRE Unicode support is enabled 01981 if ($this->isunicode AND (@preg_match('/\pL/u', 'a') == 1)) { 01982 // PCRE unicode support is turned ON 01983 // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator. 01984 // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants. 01985 // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between. 01986 //$this->setSpacesRE('/[^\S\P{Z}\P{Lo}\xa0]/u'); 01987 $this->setSpacesRE('/[^\S\P{Z}\xa0]/u'); 01988 } else { 01989 // PCRE unicode support is turned OFF 01990 $this->setSpacesRE('/[^\S\xa0]/'); 01991 } 01992 $this->default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128)); 01993 // set file ID for trailer 01994 $this->file_id = md5($this->getRandomSeed('TCPDF'.$orientation.$unit.$format.$encoding)); 01995 // set document date 01996 $this->doc_date = substr_replace(date('YmdHisO'), '\'', (0 - 2), 0).'\''; 01997 // get default graphic vars 01998 $this->default_graphic_vars = $this->getGraphicVars(); 01999 $this->header_xobj_autoreset = false; 02000 $this->custom_xmp = ''; 02001 } 02002 02008 public function __destruct() { 02009 // restore internal encoding 02010 if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) { 02011 mb_internal_encoding($this->internal_encoding); 02012 } 02013 // unset all class variables 02014 $this->_destroy(true); 02015 } 02016 02023 public function getTCPDFVersion() { 02024 return $this->tcpdf_version; 02025 } 02026 02033 public function setPageUnit($unit) { 02034 $unit = strtolower($unit); 02035 //Set scale factor 02036 switch ($unit) { 02037 // points 02038 case 'px': 02039 case 'pt': { 02040 $this->k = 1; 02041 break; 02042 } 02043 // millimeters 02044 case 'mm': { 02045 $this->k = $this->dpi / 25.4; 02046 break; 02047 } 02048 // centimeters 02049 case 'cm': { 02050 $this->k = $this->dpi / 2.54; 02051 break; 02052 } 02053 // inches 02054 case 'in': { 02055 $this->k = $this->dpi; 02056 break; 02057 } 02058 // unsupported unit 02059 default : { 02060 $this->Error('Incorrect unit: '.$unit); 02061 break; 02062 } 02063 } 02064 $this->pdfunit = $unit; 02065 if (isset($this->CurOrientation)) { 02066 $this->setPageOrientation($this->CurOrientation); 02067 } 02068 } 02069 02384 public function getPageSizeFromFormat($format) { 02385 // Paper cordinates are calculated in this way: (inches * 72) where (1 inch = 25.4 mm) 02386 switch (strtoupper($format)) { 02387 // ISO 216 A Series + 2 SIS 014711 extensions 02388 case 'A0' : {$pf = array( 2383.937, 3370.394); break;} 02389 case 'A1' : {$pf = array( 1683.780, 2383.937); break;} 02390 case 'A2' : {$pf = array( 1190.551, 1683.780); break;} 02391 case 'A3' : {$pf = array( 841.890, 1190.551); break;} 02392 case 'A4' : {$pf = array( 595.276, 841.890); break;} 02393 case 'A5' : {$pf = array( 419.528, 595.276); break;} 02394 case 'A6' : {$pf = array( 297.638, 419.528); break;} 02395 case 'A7' : {$pf = array( 209.764, 297.638); break;} 02396 case 'A8' : {$pf = array( 147.402, 209.764); break;} 02397 case 'A9' : {$pf = array( 104.882, 147.402); break;} 02398 case 'A10': {$pf = array( 73.701, 104.882); break;} 02399 case 'A11': {$pf = array( 51.024, 73.701); break;} 02400 case 'A12': {$pf = array( 36.850, 51.024); break;} 02401 // ISO 216 B Series + 2 SIS 014711 extensions 02402 case 'B0' : {$pf = array( 2834.646, 4008.189); break;} 02403 case 'B1' : {$pf = array( 2004.094, 2834.646); break;} 02404 case 'B2' : {$pf = array( 1417.323, 2004.094); break;} 02405 case 'B3' : {$pf = array( 1000.630, 1417.323); break;} 02406 case 'B4' : {$pf = array( 708.661, 1000.630); break;} 02407 case 'B5' : {$pf = array( 498.898, 708.661); break;} 02408 case 'B6' : {$pf = array( 354.331, 498.898); break;} 02409 case 'B7' : {$pf = array( 249.449, 354.331); break;} 02410 case 'B8' : {$pf = array( 175.748, 249.449); break;} 02411 case 'B9' : {$pf = array( 124.724, 175.748); break;} 02412 case 'B10': {$pf = array( 87.874, 124.724); break;} 02413 case 'B11': {$pf = array( 62.362, 87.874); break;} 02414 case 'B12': {$pf = array( 42.520, 62.362); break;} 02415 // ISO 216 C Series + 2 SIS 014711 extensions + 2 EXTENSION 02416 case 'C0' : {$pf = array( 2599.370, 3676.535); break;} 02417 case 'C1' : {$pf = array( 1836.850, 2599.370); break;} 02418 case 'C2' : {$pf = array( 1298.268, 1836.850); break;} 02419 case 'C3' : {$pf = array( 918.425, 1298.268); break;} 02420 case 'C4' : {$pf = array( 649.134, 918.425); break;} 02421 case 'C5' : {$pf = array( 459.213, 649.134); break;} 02422 case 'C6' : {$pf = array( 323.150, 459.213); break;} 02423 case 'C7' : {$pf = array( 229.606, 323.150); break;} 02424 case 'C8' : {$pf = array( 161.575, 229.606); break;} 02425 case 'C9' : {$pf = array( 113.386, 161.575); break;} 02426 case 'C10': {$pf = array( 79.370, 113.386); break;} 02427 case 'C11': {$pf = array( 56.693, 79.370); break;} 02428 case 'C12': {$pf = array( 39.685, 56.693); break;} 02429 case 'C76': {$pf = array( 229.606, 459.213); break;} 02430 case 'DL' : {$pf = array( 311.811, 623.622); break;} 02431 // SIS 014711 E Series 02432 case 'E0' : {$pf = array( 2491.654, 3517.795); break;} 02433 case 'E1' : {$pf = array( 1757.480, 2491.654); break;} 02434 case 'E2' : {$pf = array( 1247.244, 1757.480); break;} 02435 case 'E3' : {$pf = array( 878.740, 1247.244); break;} 02436 case 'E4' : {$pf = array( 623.622, 878.740); break;} 02437 case 'E5' : {$pf = array( 439.370, 623.622); break;} 02438 case 'E6' : {$pf = array( 311.811, 439.370); break;} 02439 case 'E7' : {$pf = array( 221.102, 311.811); break;} 02440 case 'E8' : {$pf = array( 155.906, 221.102); break;} 02441 case 'E9' : {$pf = array( 110.551, 155.906); break;} 02442 case 'E10': {$pf = array( 76.535, 110.551); break;} 02443 case 'E11': {$pf = array( 53.858, 76.535); break;} 02444 case 'E12': {$pf = array( 36.850, 53.858); break;} 02445 // SIS 014711 G Series 02446 case 'G0' : {$pf = array( 2715.591, 3838.110); break;} 02447 case 'G1' : {$pf = array( 1919.055, 2715.591); break;} 02448 case 'G2' : {$pf = array( 1357.795, 1919.055); break;} 02449 case 'G3' : {$pf = array( 958.110, 1357.795); break;} 02450 case 'G4' : {$pf = array( 677.480, 958.110); break;} 02451 case 'G5' : {$pf = array( 479.055, 677.480); break;} 02452 case 'G6' : {$pf = array( 337.323, 479.055); break;} 02453 case 'G7' : {$pf = array( 238.110, 337.323); break;} 02454 case 'G8' : {$pf = array( 167.244, 238.110); break;} 02455 case 'G9' : {$pf = array( 119.055, 167.244); break;} 02456 case 'G10': {$pf = array( 82.205, 119.055); break;} 02457 case 'G11': {$pf = array( 59.528, 82.205); break;} 02458 case 'G12': {$pf = array( 39.685, 59.528); break;} 02459 // ISO Press 02460 case 'RA0': {$pf = array( 2437.795, 3458.268); break;} 02461 case 'RA1': {$pf = array( 1729.134, 2437.795); break;} 02462 case 'RA2': {$pf = array( 1218.898, 1729.134); break;} 02463 case 'RA3': {$pf = array( 864.567, 1218.898); break;} 02464 case 'RA4': {$pf = array( 609.449, 864.567); break;} 02465 case 'SRA0': {$pf = array( 2551.181, 3628.346); break;} 02466 case 'SRA1': {$pf = array( 1814.173, 2551.181); break;} 02467 case 'SRA2': {$pf = array( 1275.591, 1814.173); break;} 02468 case 'SRA3': {$pf = array( 907.087, 1275.591); break;} 02469 case 'SRA4': {$pf = array( 637.795, 907.087); break;} 02470 // German DIN 476 02471 case '4A0': {$pf = array( 4767.874, 6740.787); break;} 02472 case '2A0': {$pf = array( 3370.394, 4767.874); break;} 02473 // Variations on the ISO Standard 02474 case 'A2_EXTRA' : {$pf = array( 1261.417, 1754.646); break;} 02475 case 'A3+' : {$pf = array( 932.598, 1369.134); break;} 02476 case 'A3_EXTRA' : {$pf = array( 912.756, 1261.417); break;} 02477 case 'A3_SUPER' : {$pf = array( 864.567, 1440.000); break;} 02478 case 'SUPER_A3' : {$pf = array( 864.567, 1380.472); break;} 02479 case 'A4_EXTRA' : {$pf = array( 666.142, 912.756); break;} 02480 case 'A4_SUPER' : {$pf = array( 649.134, 912.756); break;} 02481 case 'SUPER_A4' : {$pf = array( 643.465, 1009.134); break;} 02482 case 'A4_LONG' : {$pf = array( 595.276, 986.457); break;} 02483 case 'F4' : {$pf = array( 595.276, 935.433); break;} 02484 case 'SO_B5_EXTRA': {$pf = array( 572.598, 782.362); break;} 02485 case 'A5_EXTRA' : {$pf = array( 490.394, 666.142); break;} 02486 // ANSI Series 02487 case 'ANSI_E': {$pf = array( 2448.000, 3168.000); break;} 02488 case 'ANSI_D': {$pf = array( 1584.000, 2448.000); break;} 02489 case 'ANSI_C': {$pf = array( 1224.000, 1584.000); break;} 02490 case 'ANSI_B': {$pf = array( 792.000, 1224.000); break;} 02491 case 'ANSI_A': {$pf = array( 612.000, 792.000); break;} 02492 // Traditional 'Loose' North American Paper Sizes 02493 case 'USLEDGER': 02494 case 'LEDGER' : {$pf = array( 1224.000, 792.000); break;} 02495 case 'ORGANIZERK': 02496 case 'BIBLE': 02497 case 'USTABLOID': 02498 case 'TABLOID': {$pf = array( 792.000, 1224.000); break;} 02499 case 'ORGANIZERM': 02500 case 'USLETTER': 02501 case 'LETTER' : {$pf = array( 612.000, 792.000); break;} 02502 case 'USLEGAL': 02503 case 'LEGAL' : {$pf = array( 612.000, 1008.000); break;} 02504 case 'GOVERNMENTLETTER': 02505 case 'GLETTER': {$pf = array( 576.000, 756.000); break;} 02506 case 'JUNIORLEGAL': 02507 case 'JLEGAL' : {$pf = array( 576.000, 360.000); break;} 02508 // Other North American Paper Sizes 02509 case 'QUADDEMY': {$pf = array( 2520.000, 3240.000); break;} 02510 case 'SUPER_B': {$pf = array( 936.000, 1368.000); break;} 02511 case 'QUARTO': {$pf = array( 648.000, 792.000); break;} 02512 case 'GOVERNMENTLEGAL': 02513 case 'FOLIO': {$pf = array( 612.000, 936.000); break;} 02514 case 'MONARCH': 02515 case 'EXECUTIVE': {$pf = array( 522.000, 756.000); break;} 02516 case 'ORGANIZERL': 02517 case 'STATEMENT': 02518 case 'MEMO': {$pf = array( 396.000, 612.000); break;} 02519 case 'FOOLSCAP': {$pf = array( 595.440, 936.000); break;} 02520 case 'COMPACT': {$pf = array( 306.000, 486.000); break;} 02521 case 'ORGANIZERJ': {$pf = array( 198.000, 360.000); break;} 02522 // Canadian standard CAN 2-9.60M 02523 case 'P1': {$pf = array( 1587.402, 2437.795); break;} 02524 case 'P2': {$pf = array( 1218.898, 1587.402); break;} 02525 case 'P3': {$pf = array( 793.701, 1218.898); break;} 02526 case 'P4': {$pf = array( 609.449, 793.701); break;} 02527 case 'P5': {$pf = array( 396.850, 609.449); break;} 02528 case 'P6': {$pf = array( 303.307, 396.850); break;} 02529 // North American Architectural Sizes 02530 case 'ARCH_E' : {$pf = array( 2592.000, 3456.000); break;} 02531 case 'ARCH_E1': {$pf = array( 2160.000, 3024.000); break;} 02532 case 'ARCH_D' : {$pf = array( 1728.000, 2592.000); break;} 02533 case 'BROADSHEET': 02534 case 'ARCH_C' : {$pf = array( 1296.000, 1728.000); break;} 02535 case 'ARCH_B' : {$pf = array( 864.000, 1296.000); break;} 02536 case 'ARCH_A' : {$pf = array( 648.000, 864.000); break;} 02537 // --- North American Envelope Sizes --- 02538 // - Announcement Envelopes 02539 case 'ANNENV_A2' : {$pf = array( 314.640, 414.000); break;} 02540 case 'ANNENV_A6' : {$pf = array( 342.000, 468.000); break;} 02541 case 'ANNENV_A7' : {$pf = array( 378.000, 522.000); break;} 02542 case 'ANNENV_A8' : {$pf = array( 396.000, 584.640); break;} 02543 case 'ANNENV_A10' : {$pf = array( 450.000, 692.640); break;} 02544 case 'ANNENV_SLIM': {$pf = array( 278.640, 638.640); break;} 02545 // - Commercial Envelopes 02546 case 'COMMENV_N6_1/4': {$pf = array( 252.000, 432.000); break;} 02547 case 'COMMENV_N6_3/4': {$pf = array( 260.640, 468.000); break;} 02548 case 'COMMENV_N8' : {$pf = array( 278.640, 540.000); break;} 02549 case 'COMMENV_N9' : {$pf = array( 278.640, 638.640); break;} 02550 case 'COMMENV_N10' : {$pf = array( 296.640, 684.000); break;} 02551 case 'COMMENV_N11' : {$pf = array( 324.000, 746.640); break;} 02552 case 'COMMENV_N12' : {$pf = array( 342.000, 792.000); break;} 02553 case 'COMMENV_N14' : {$pf = array( 360.000, 828.000); break;} 02554 // - Catalogue Envelopes 02555 case 'CATENV_N1' : {$pf = array( 432.000, 648.000); break;} 02556 case 'CATENV_N1_3/4' : {$pf = array( 468.000, 684.000); break;} 02557 case 'CATENV_N2' : {$pf = array( 468.000, 720.000); break;} 02558 case 'CATENV_N3' : {$pf = array( 504.000, 720.000); break;} 02559 case 'CATENV_N6' : {$pf = array( 540.000, 756.000); break;} 02560 case 'CATENV_N7' : {$pf = array( 576.000, 792.000); break;} 02561 case 'CATENV_N8' : {$pf = array( 594.000, 810.000); break;} 02562 case 'CATENV_N9_1/2' : {$pf = array( 612.000, 756.000); break;} 02563 case 'CATENV_N9_3/4' : {$pf = array( 630.000, 810.000); break;} 02564 case 'CATENV_N10_1/2': {$pf = array( 648.000, 864.000); break;} 02565 case 'CATENV_N12_1/2': {$pf = array( 684.000, 900.000); break;} 02566 case 'CATENV_N13_1/2': {$pf = array( 720.000, 936.000); break;} 02567 case 'CATENV_N14_1/4': {$pf = array( 810.000, 882.000); break;} 02568 case 'CATENV_N14_1/2': {$pf = array( 828.000, 1044.000); break;} 02569 // Japanese (JIS P 0138-61) Standard B-Series 02570 case 'JIS_B0' : {$pf = array( 2919.685, 4127.244); break;} 02571 case 'JIS_B1' : {$pf = array( 2063.622, 2919.685); break;} 02572 case 'JIS_B2' : {$pf = array( 1459.843, 2063.622); break;} 02573 case 'JIS_B3' : {$pf = array( 1031.811, 1459.843); break;} 02574 case 'JIS_B4' : {$pf = array( 728.504, 1031.811); break;} 02575 case 'JIS_B5' : {$pf = array( 515.906, 728.504); break;} 02576 case 'JIS_B6' : {$pf = array( 362.835, 515.906); break;} 02577 case 'JIS_B7' : {$pf = array( 257.953, 362.835); break;} 02578 case 'JIS_B8' : {$pf = array( 181.417, 257.953); break;} 02579 case 'JIS_B9' : {$pf = array( 127.559, 181.417); break;} 02580 case 'JIS_B10': {$pf = array( 90.709, 127.559); break;} 02581 case 'JIS_B11': {$pf = array( 62.362, 90.709); break;} 02582 case 'JIS_B12': {$pf = array( 45.354, 62.362); break;} 02583 // PA Series 02584 case 'PA0' : {$pf = array( 2381.102, 3174.803,); break;} 02585 case 'PA1' : {$pf = array( 1587.402, 2381.102); break;} 02586 case 'PA2' : {$pf = array( 1190.551, 1587.402); break;} 02587 case 'PA3' : {$pf = array( 793.701, 1190.551); break;} 02588 case 'PA4' : {$pf = array( 595.276, 793.701); break;} 02589 case 'PA5' : {$pf = array( 396.850, 595.276); break;} 02590 case 'PA6' : {$pf = array( 297.638, 396.850); break;} 02591 case 'PA7' : {$pf = array( 198.425, 297.638); break;} 02592 case 'PA8' : {$pf = array( 147.402, 198.425); break;} 02593 case 'PA9' : {$pf = array( 99.213, 147.402); break;} 02594 case 'PA10': {$pf = array( 73.701, 99.213); break;} 02595 // Standard Photographic Print Sizes 02596 case 'PASSPORT_PHOTO': {$pf = array( 99.213, 127.559); break;} 02597 case 'E' : {$pf = array( 233.858, 340.157); break;} 02598 case 'L': 02599 case '3R' : {$pf = array( 252.283, 360.000); break;} 02600 case 'KG': 02601 case '4R' : {$pf = array( 289.134, 430.866); break;} 02602 case '4D' : {$pf = array( 340.157, 430.866); break;} 02603 case '2L': 02604 case '5R' : {$pf = array( 360.000, 504.567); break;} 02605 case '8P': 02606 case '6R' : {$pf = array( 430.866, 575.433); break;} 02607 case '6P': 02608 case '8R' : {$pf = array( 575.433, 720.000); break;} 02609 case '6PW': 02610 case 'S8R' : {$pf = array( 575.433, 864.567); break;} 02611 case '4P': 02612 case '10R' : {$pf = array( 720.000, 864.567); break;} 02613 case '4PW': 02614 case 'S10R': {$pf = array( 720.000, 1080.000); break;} 02615 case '11R' : {$pf = array( 790.866, 1009.134); break;} 02616 case 'S11R': {$pf = array( 790.866, 1224.567); break;} 02617 case '12R' : {$pf = array( 864.567, 1080.000); break;} 02618 case 'S12R': {$pf = array( 864.567, 1292.598); break;} 02619 // Common Newspaper Sizes 02620 case 'NEWSPAPER_BROADSHEET': {$pf = array( 2125.984, 1700.787); break;} 02621 case 'NEWSPAPER_BERLINER' : {$pf = array( 1332.283, 892.913); break;} 02622 case 'NEWSPAPER_TABLOID': 02623 case 'NEWSPAPER_COMPACT' : {$pf = array( 1218.898, 793.701); break;} 02624 // Business Cards 02625 case 'CREDIT_CARD': 02626 case 'BUSINESS_CARD': 02627 case 'BUSINESS_CARD_ISO7810': {$pf = array( 153.014, 242.646); break;} 02628 case 'BUSINESS_CARD_ISO216' : {$pf = array( 147.402, 209.764); break;} 02629 case 'BUSINESS_CARD_IT': 02630 case 'BUSINESS_CARD_UK': 02631 case 'BUSINESS_CARD_FR': 02632 case 'BUSINESS_CARD_DE': 02633 case 'BUSINESS_CARD_ES' : {$pf = array( 155.906, 240.945); break;} 02634 case 'BUSINESS_CARD_CA': 02635 case 'BUSINESS_CARD_US' : {$pf = array( 144.567, 252.283); break;} 02636 case 'BUSINESS_CARD_JP' : {$pf = array( 155.906, 257.953); break;} 02637 case 'BUSINESS_CARD_HK' : {$pf = array( 153.071, 255.118); break;} 02638 case 'BUSINESS_CARD_AU': 02639 case 'BUSINESS_CARD_DK': 02640 case 'BUSINESS_CARD_SE' : {$pf = array( 155.906, 255.118); break;} 02641 case 'BUSINESS_CARD_RU': 02642 case 'BUSINESS_CARD_CZ': 02643 case 'BUSINESS_CARD_FI': 02644 case 'BUSINESS_CARD_HU': 02645 case 'BUSINESS_CARD_IL' : {$pf = array( 141.732, 255.118); break;} 02646 // Billboards 02647 case '4SHEET' : {$pf = array( 2880.000, 4320.000); break;} 02648 case '6SHEET' : {$pf = array( 3401.575, 5102.362); break;} 02649 case '12SHEET': {$pf = array( 8640.000, 4320.000); break;} 02650 case '16SHEET': {$pf = array( 5760.000, 8640.000); break;} 02651 case '32SHEET': {$pf = array(11520.000, 8640.000); break;} 02652 case '48SHEET': {$pf = array(17280.000, 8640.000); break;} 02653 case '64SHEET': {$pf = array(23040.000, 8640.000); break;} 02654 case '96SHEET': {$pf = array(34560.000, 8640.000); break;} 02655 // Old European Sizes 02656 // - Old Imperial English Sizes 02657 case 'EN_EMPEROR' : {$pf = array( 3456.000, 5184.000); break;} 02658 case 'EN_ANTIQUARIAN' : {$pf = array( 2232.000, 3816.000); break;} 02659 case 'EN_GRAND_EAGLE' : {$pf = array( 2070.000, 3024.000); break;} 02660 case 'EN_DOUBLE_ELEPHANT' : {$pf = array( 1926.000, 2880.000); break;} 02661 case 'EN_ATLAS' : {$pf = array( 1872.000, 2448.000); break;} 02662 case 'EN_COLOMBIER' : {$pf = array( 1692.000, 2484.000); break;} 02663 case 'EN_ELEPHANT' : {$pf = array( 1656.000, 2016.000); break;} 02664 case 'EN_DOUBLE_DEMY' : {$pf = array( 1620.000, 2556.000); break;} 02665 case 'EN_IMPERIAL' : {$pf = array( 1584.000, 2160.000); break;} 02666 case 'EN_PRINCESS' : {$pf = array( 1548.000, 2016.000); break;} 02667 case 'EN_CARTRIDGE' : {$pf = array( 1512.000, 1872.000); break;} 02668 case 'EN_DOUBLE_LARGE_POST': {$pf = array( 1512.000, 2376.000); break;} 02669 case 'EN_ROYAL' : {$pf = array( 1440.000, 1800.000); break;} 02670 case 'EN_SHEET': 02671 case 'EN_HALF_POST' : {$pf = array( 1404.000, 1692.000); break;} 02672 case 'EN_SUPER_ROYAL' : {$pf = array( 1368.000, 1944.000); break;} 02673 case 'EN_DOUBLE_POST' : {$pf = array( 1368.000, 2196.000); break;} 02674 case 'EN_MEDIUM' : {$pf = array( 1260.000, 1656.000); break;} 02675 case 'EN_DEMY' : {$pf = array( 1260.000, 1620.000); break;} 02676 case 'EN_LARGE_POST' : {$pf = array( 1188.000, 1512.000); break;} 02677 case 'EN_COPY_DRAUGHT' : {$pf = array( 1152.000, 1440.000); break;} 02678 case 'EN_POST' : {$pf = array( 1116.000, 1386.000); break;} 02679 case 'EN_CROWN' : {$pf = array( 1080.000, 1440.000); break;} 02680 case 'EN_PINCHED_POST' : {$pf = array( 1062.000, 1332.000); break;} 02681 case 'EN_BRIEF' : {$pf = array( 972.000, 1152.000); break;} 02682 case 'EN_FOOLSCAP' : {$pf = array( 972.000, 1224.000); break;} 02683 case 'EN_SMALL_FOOLSCAP' : {$pf = array( 954.000, 1188.000); break;} 02684 case 'EN_POTT' : {$pf = array( 900.000, 1080.000); break;} 02685 // - Old Imperial Belgian Sizes 02686 case 'BE_GRAND_AIGLE' : {$pf = array( 1984.252, 2948.031); break;} 02687 case 'BE_COLOMBIER' : {$pf = array( 1757.480, 2409.449); break;} 02688 case 'BE_DOUBLE_CARRE': {$pf = array( 1757.480, 2607.874); break;} 02689 case 'BE_ELEPHANT' : {$pf = array( 1746.142, 2182.677); break;} 02690 case 'BE_PETIT_AIGLE' : {$pf = array( 1700.787, 2381.102); break;} 02691 case 'BE_GRAND_JESUS' : {$pf = array( 1559.055, 2069.291); break;} 02692 case 'BE_JESUS' : {$pf = array( 1530.709, 2069.291); break;} 02693 case 'BE_RAISIN' : {$pf = array( 1417.323, 1842.520); break;} 02694 case 'BE_GRAND_MEDIAN': {$pf = array( 1303.937, 1714.961); break;} 02695 case 'BE_DOUBLE_POSTE': {$pf = array( 1233.071, 1601.575); break;} 02696 case 'BE_COQUILLE' : {$pf = array( 1218.898, 1587.402); break;} 02697 case 'BE_PETIT_MEDIAN': {$pf = array( 1176.378, 1502.362); break;} 02698 case 'BE_RUCHE' : {$pf = array( 1020.472, 1303.937); break;} 02699 case 'BE_PROPATRIA' : {$pf = array( 977.953, 1218.898); break;} 02700 case 'BE_LYS' : {$pf = array( 898.583, 1125.354); break;} 02701 case 'BE_POT' : {$pf = array( 870.236, 1088.504); break;} 02702 case 'BE_ROSETTE' : {$pf = array( 765.354, 983.622); break;} 02703 // - Old Imperial French Sizes 02704 case 'FR_UNIVERS' : {$pf = array( 2834.646, 3685.039); break;} 02705 case 'FR_DOUBLE_COLOMBIER' : {$pf = array( 2551.181, 3571.654); break;} 02706 case 'FR_GRANDE_MONDE' : {$pf = array( 2551.181, 3571.654); break;} 02707 case 'FR_DOUBLE_SOLEIL' : {$pf = array( 2267.717, 3401.575); break;} 02708 case 'FR_DOUBLE_JESUS' : {$pf = array( 2154.331, 3174.803); break;} 02709 case 'FR_GRAND_AIGLE' : {$pf = array( 2125.984, 3004.724); break;} 02710 case 'FR_PETIT_AIGLE' : {$pf = array( 1984.252, 2664.567); break;} 02711 case 'FR_DOUBLE_RAISIN' : {$pf = array( 1842.520, 2834.646); break;} 02712 case 'FR_JOURNAL' : {$pf = array( 1842.520, 2664.567); break;} 02713 case 'FR_COLOMBIER_AFFICHE': {$pf = array( 1785.827, 2551.181); break;} 02714 case 'FR_DOUBLE_CAVALIER' : {$pf = array( 1757.480, 2607.874); break;} 02715 case 'FR_CLOCHE' : {$pf = array( 1700.787, 2267.717); break;} 02716 case 'FR_SOLEIL' : {$pf = array( 1700.787, 2267.717); break;} 02717 case 'FR_DOUBLE_CARRE' : {$pf = array( 1587.402, 2551.181); break;} 02718 case 'FR_DOUBLE_COQUILLE' : {$pf = array( 1587.402, 2494.488); break;} 02719 case 'FR_JESUS' : {$pf = array( 1587.402, 2154.331); break;} 02720 case 'FR_RAISIN' : {$pf = array( 1417.323, 1842.520); break;} 02721 case 'FR_CAVALIER' : {$pf = array( 1303.937, 1757.480); break;} 02722 case 'FR_DOUBLE_COURONNE' : {$pf = array( 1303.937, 2040.945); break;} 02723 case 'FR_CARRE' : {$pf = array( 1275.591, 1587.402); break;} 02724 case 'FR_COQUILLE' : {$pf = array( 1247.244, 1587.402); break;} 02725 case 'FR_DOUBLE_TELLIERE' : {$pf = array( 1247.244, 1927.559); break;} 02726 case 'FR_DOUBLE_CLOCHE' : {$pf = array( 1133.858, 1700.787); break;} 02727 case 'FR_DOUBLE_POT' : {$pf = array( 1133.858, 1757.480); break;} 02728 case 'FR_ECU' : {$pf = array( 1133.858, 1474.016); break;} 02729 case 'FR_COURONNE' : {$pf = array( 1020.472, 1303.937); break;} 02730 case 'FR_TELLIERE' : {$pf = array( 963.780, 1247.244); break;} 02731 case 'FR_POT' : {$pf = array( 878.740, 1133.858); break;} 02732 // DEFAULT ISO A4 02733 default: {$pf = array( 595.276, 841.890); break;} 02734 } 02735 return $pf; 02736 } 02737 02793 protected function setPageFormat($format, $orientation='P') { 02794 if (!empty($format) AND isset($this->pagedim[$this->page])) { 02795 // remove inherited values 02796 unset($this->pagedim[$this->page]); 02797 } 02798 if (is_string($format)) { 02799 // get page measures from format name 02800 $pf = $this->getPageSizeFromFormat($format); 02801 $this->fwPt = $pf[0]; 02802 $this->fhPt = $pf[1]; 02803 } else { 02804 // the boundaries of the physical medium on which the page shall be displayed or printed 02805 if (isset($format['MediaBox'])) { 02806 $this->setPageBoxes($this->page, 'MediaBox', $format['MediaBox']['llx'], $format['MediaBox']['lly'], $format['MediaBox']['urx'], $format['MediaBox']['ury'], false); 02807 $this->fwPt = (($format['MediaBox']['urx'] - $format['MediaBox']['llx']) * $this->k); 02808 $this->fhPt = (($format['MediaBox']['ury'] - $format['MediaBox']['lly']) * $this->k); 02809 } else { 02810 if (isset($format[0]) AND is_numeric($format[0]) AND isset($format[1]) AND is_numeric($format[1])) { 02811 $pf = array(($format[0] * $this->k), ($format[1] * $this->k)); 02812 } else { 02813 if (!isset($format['format'])) { 02814 // default value 02815 $format['format'] = 'A4'; 02816 } 02817 $pf = $this->getPageSizeFromFormat($format['format']); 02818 } 02819 $this->fwPt = $pf[0]; 02820 $this->fhPt = $pf[1]; 02821 $this->setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true); 02822 } 02823 // the visible region of default user space 02824 if (isset($format['CropBox'])) { 02825 $this->setPageBoxes($this->page, 'CropBox', $format['CropBox']['llx'], $format['CropBox']['lly'], $format['CropBox']['urx'], $format['CropBox']['ury'], false); 02826 } 02827 // the region to which the contents of the page shall be clipped when output in a production environment 02828 if (isset($format['BleedBox'])) { 02829 $this->setPageBoxes($this->page, 'BleedBox', $format['BleedBox']['llx'], $format['BleedBox']['lly'], $format['BleedBox']['urx'], $format['BleedBox']['ury'], false); 02830 } 02831 // the intended dimensions of the finished page after trimming 02832 if (isset($format['TrimBox'])) { 02833 $this->setPageBoxes($this->page, 'TrimBox', $format['TrimBox']['llx'], $format['TrimBox']['lly'], $format['TrimBox']['urx'], $format['TrimBox']['ury'], false); 02834 } 02835 // the page's meaningful content (including potential white space) 02836 if (isset($format['ArtBox'])) { 02837 $this->setPageBoxes($this->page, 'ArtBox', $format['ArtBox']['llx'], $format['ArtBox']['lly'], $format['ArtBox']['urx'], $format['ArtBox']['ury'], false); 02838 } 02839 // specify the colours and other visual characteristics that should be used in displaying guidelines on the screen for the various page boundaries 02840 if (isset($format['BoxColorInfo'])) { 02841 $this->pagedim[$this->page]['BoxColorInfo'] = $format['BoxColorInfo']; 02842 } 02843 if (isset($format['Rotate']) AND (($format['Rotate'] % 90) == 0)) { 02844 // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90. 02845 $this->pagedim[$this->page]['Rotate'] = intval($format['Rotate']); 02846 } 02847 if (isset($format['PZ'])) { 02848 // The page's preferred zoom (magnification) factor 02849 $this->pagedim[$this->page]['PZ'] = floatval($format['PZ']); 02850 } 02851 if (isset($format['trans'])) { 02852 // The style and duration of the visual transition to use when moving from another page to the given page during a presentation 02853 if (isset($format['trans']['Dur'])) { 02854 // The page's display duration 02855 $this->pagedim[$this->page]['trans']['Dur'] = floatval($format['trans']['Dur']); 02856 } 02857 $stansition_styles = array('Split', 'Blinds', 'Box', 'Wipe', 'Dissolve', 'Glitter', 'R', 'Fly', 'Push', 'Cover', 'Uncover', 'Fade'); 02858 if (isset($format['trans']['S']) AND in_array($format['trans']['S'], $stansition_styles)) { 02859 // The transition style that shall be used when moving to this page from another during a presentation 02860 $this->pagedim[$this->page]['trans']['S'] = $format['trans']['S']; 02861 $valid_effect = array('Split', 'Blinds'); 02862 $valid_vals = array('H', 'V'); 02863 if (isset($format['trans']['Dm']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['Dm'], $valid_vals)) { 02864 $this->pagedim[$this->page]['trans']['Dm'] = $format['trans']['Dm']; 02865 } 02866 $valid_effect = array('Split', 'Box', 'Fly'); 02867 $valid_vals = array('I', 'O'); 02868 if (isset($format['trans']['M']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['M'], $valid_vals)) { 02869 $this->pagedim[$this->page]['trans']['M'] = $format['trans']['M']; 02870 } 02871 $valid_effect = array('Wipe', 'Glitter', 'Fly', 'Cover', 'Uncover', 'Push'); 02872 if (isset($format['trans']['Di']) AND in_array($format['trans']['S'], $valid_effect)) { 02873 if (((($format['trans']['Di'] == 90) OR ($format['trans']['Di'] == 180)) AND ($format['trans']['S'] == 'Wipe')) 02874 OR (($format['trans']['Di'] == 315) AND ($format['trans']['S'] == 'Glitter')) 02875 OR (($format['trans']['Di'] == 0) OR ($format['trans']['Di'] == 270))) { 02876 $this->pagedim[$this->page]['trans']['Di'] = intval($format['trans']['Di']); 02877 } 02878 } 02879 if (isset($format['trans']['SS']) AND ($format['trans']['S'] == 'Fly')) { 02880 $this->pagedim[$this->page]['trans']['SS'] = floatval($format['trans']['SS']); 02881 } 02882 if (isset($format['trans']['B']) AND ($format['trans']['B'] === true) AND ($format['trans']['S'] == 'Fly')) { 02883 $this->pagedim[$this->page]['trans']['B'] = 'true'; 02884 } 02885 } else { 02886 $this->pagedim[$this->page]['trans']['S'] = 'R'; 02887 } 02888 if (isset($format['trans']['D'])) { 02889 // The duration of the transition effect, in seconds 02890 $this->pagedim[$this->page]['trans']['D'] = floatval($format['trans']['D']); 02891 } else { 02892 $this->pagedim[$this->page]['trans']['D'] = 1; 02893 } 02894 } 02895 } 02896 $this->setPageOrientation($orientation); 02897 } 02898 02911 public function setPageBoxes($page, $type, $llx, $lly, $urx, $ury, $points=false) { 02912 if (!isset($this->pagedim[$page])) { 02913 // initialize array 02914 $this->pagedim[$page] = array(); 02915 } 02916 $pageboxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox'); 02917 if (!in_array($type, $pageboxes)) { 02918 return; 02919 } 02920 if ($points) { 02921 $k = 1; 02922 } else { 02923 $k = $this->k; 02924 } 02925 $this->pagedim[$page][$type]['llx'] = ($llx * $k); 02926 $this->pagedim[$page][$type]['lly'] = ($lly * $k); 02927 $this->pagedim[$page][$type]['urx'] = ($urx * $k); 02928 $this->pagedim[$page][$type]['ury'] = ($ury * $k); 02929 } 02930 02937 protected function swapPageBoxCoordinates($page) { 02938 $pageboxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox'); 02939 foreach ($pageboxes as $type) { 02940 // swap X and Y coordinates 02941 if (isset($this->pagedim[$page][$type])) { 02942 $tmp = $this->pagedim[$page][$type]['llx']; 02943 $this->pagedim[$page][$type]['llx'] = $this->pagedim[$page][$type]['lly']; 02944 $this->pagedim[$page][$type]['lly'] = $tmp; 02945 $tmp = $this->pagedim[$page][$type]['urx']; 02946 $this->pagedim[$page][$type]['urx'] = $this->pagedim[$page][$type]['ury']; 02947 $this->pagedim[$page][$type]['ury'] = $tmp; 02948 } 02949 } 02950 } 02951 02960 public function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') { 02961 if (!isset($this->pagedim[$this->page]['MediaBox'])) { 02962 // the boundaries of the physical medium on which the page shall be displayed or printed 02963 $this->setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true); 02964 } 02965 if (!isset($this->pagedim[$this->page]['CropBox'])) { 02966 // the visible region of default user space 02967 $this->setPageBoxes($this->page, 'CropBox', $this->pagedim[$this->page]['MediaBox']['llx'], $this->pagedim[$this->page]['MediaBox']['lly'], $this->pagedim[$this->page]['MediaBox']['urx'], $this->pagedim[$this->page]['MediaBox']['ury'], true); 02968 } 02969 if (!isset($this->pagedim[$this->page]['BleedBox'])) { 02970 // the region to which the contents of the page shall be clipped when output in a production environment 02971 $this->setPageBoxes($this->page, 'BleedBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true); 02972 } 02973 if (!isset($this->pagedim[$this->page]['TrimBox'])) { 02974 // the intended dimensions of the finished page after trimming 02975 $this->setPageBoxes($this->page, 'TrimBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true); 02976 } 02977 if (!isset($this->pagedim[$this->page]['ArtBox'])) { 02978 // the page's meaningful content (including potential white space) 02979 $this->setPageBoxes($this->page, 'ArtBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true); 02980 } 02981 if (!isset($this->pagedim[$this->page]['Rotate'])) { 02982 // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90. 02983 $this->pagedim[$this->page]['Rotate'] = 0; 02984 } 02985 if (!isset($this->pagedim[$this->page]['PZ'])) { 02986 // The page's preferred zoom (magnification) factor 02987 $this->pagedim[$this->page]['PZ'] = 1; 02988 } 02989 if ($this->fwPt > $this->fhPt) { 02990 // landscape 02991 $default_orientation = 'L'; 02992 } else { 02993 // portrait 02994 $default_orientation = 'P'; 02995 } 02996 $valid_orientations = array('P', 'L'); 02997 if (empty($orientation)) { 02998 $orientation = $default_orientation; 02999 } else { 03000 $orientation = strtoupper($orientation{0}); 03001 } 03002 if (in_array($orientation, $valid_orientations) AND ($orientation != $default_orientation)) { 03003 $this->CurOrientation = $orientation; 03004 $this->wPt = $this->fhPt; 03005 $this->hPt = $this->fwPt; 03006 } else { 03007 $this->CurOrientation = $default_orientation; 03008 $this->wPt = $this->fwPt; 03009 $this->hPt = $this->fhPt; 03010 } 03011 if ((abs($this->pagedim[$this->page]['MediaBox']['urx'] - $this->hPt) < $this->feps) AND (abs($this->pagedim[$this->page]['MediaBox']['ury'] - $this->wPt) < $this->feps)){ 03012 // swap X and Y coordinates (change page orientation) 03013 $this->swapPageBoxCoordinates($this->page); 03014 } 03015 $this->w = $this->wPt / $this->k; 03016 $this->h = $this->hPt / $this->k; 03017 if ($this->empty_string($autopagebreak)) { 03018 if (isset($this->AutoPageBreak)) { 03019 $autopagebreak = $this->AutoPageBreak; 03020 } else { 03021 $autopagebreak = true; 03022 } 03023 } 03024 if ($this->empty_string($bottommargin)) { 03025 if (isset($this->bMargin)) { 03026 $bottommargin = $this->bMargin; 03027 } else { 03028 // default value = 2 cm 03029 $bottommargin = 2 * 28.35 / $this->k; 03030 } 03031 } 03032 $this->SetAutoPageBreak($autopagebreak, $bottommargin); 03033 // store page dimensions 03034 $this->pagedim[$this->page]['w'] = $this->wPt; 03035 $this->pagedim[$this->page]['h'] = $this->hPt; 03036 $this->pagedim[$this->page]['wk'] = $this->w; 03037 $this->pagedim[$this->page]['hk'] = $this->h; 03038 $this->pagedim[$this->page]['tm'] = $this->tMargin; 03039 $this->pagedim[$this->page]['bm'] = $bottommargin; 03040 $this->pagedim[$this->page]['lm'] = $this->lMargin; 03041 $this->pagedim[$this->page]['rm'] = $this->rMargin; 03042 $this->pagedim[$this->page]['pb'] = $autopagebreak; 03043 $this->pagedim[$this->page]['or'] = $this->CurOrientation; 03044 $this->pagedim[$this->page]['olm'] = $this->original_lMargin; 03045 $this->pagedim[$this->page]['orm'] = $this->original_rMargin; 03046 } 03047 03065 public function setSpacesRE($re='/[^\S\xa0]/') { 03066 $this->re_spaces = $re; 03067 $re_parts = explode('/', $re); 03068 // get pattern parts 03069 $this->re_space = array(); 03070 if (isset($re_parts[1]) AND !empty($re_parts[1])) { 03071 $this->re_space['p'] = $re_parts[1]; 03072 } else { 03073 $this->re_space['p'] = '[\s]'; 03074 } 03075 // set pattern modifiers 03076 if (isset($re_parts[2]) AND !empty($re_parts[2])) { 03077 $this->re_space['m'] = $re_parts[2]; 03078 } else { 03079 $this->re_space['m'] = ''; 03080 } 03081 } 03082 03090 public function setRTL($enable, $resetx=true) { 03091 $enable = $enable ? true : false; 03092 $resetx = ($resetx AND ($enable != $this->rtl)); 03093 $this->rtl = $enable; 03094 $this->tmprtl = false; 03095 if ($resetx) { 03096 $this->Ln(0); 03097 } 03098 } 03099 03106 public function getRTL() { 03107 return $this->rtl; 03108 } 03109 03116 public function setTempRTL($mode) { 03117 $newmode = false; 03118 switch (strtoupper($mode)) { 03119 case 'LTR': 03120 case 'L': { 03121 if ($this->rtl) { 03122 $newmode = 'L'; 03123 } 03124 break; 03125 } 03126 case 'RTL': 03127 case 'R': { 03128 if (!$this->rtl) { 03129 $newmode = 'R'; 03130 } 03131 break; 03132 } 03133 case false: 03134 default: { 03135 $newmode = false; 03136 break; 03137 } 03138 } 03139 $this->tmprtl = $newmode; 03140 } 03141 03148 public function isRTLTextDir() { 03149 return ($this->rtl OR ($this->tmprtl == 'R')); 03150 } 03151 03159 public function setLastH($h) { 03160 $this->lasth = $h; 03161 } 03162 03168 public function resetLastH() { 03169 $this->lasth = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B']; 03170 } 03171 03178 public function getLastH() { 03179 return $this->lasth; 03180 } 03181 03189 public function setImageScale($scale) { 03190 $this->imgscale = $scale; 03191 } 03192 03200 public function getImageScale() { 03201 return $this->imgscale; 03202 } 03203 03213 public function getPageDimensions($pagenum='') { 03214 if (empty($pagenum)) { 03215 $pagenum = $this->page; 03216 } 03217 return $this->pagedim[$pagenum]; 03218 } 03219 03229 public function getPageWidth($pagenum='') { 03230 if (empty($pagenum)) { 03231 return $this->w; 03232 } 03233 return $this->pagedim[$pagenum]['w']; 03234 } 03235 03245 public function getPageHeight($pagenum='') { 03246 if (empty($pagenum)) { 03247 return $this->h; 03248 } 03249 return $this->pagedim[$pagenum]['h']; 03250 } 03251 03261 public function getBreakMargin($pagenum='') { 03262 if (empty($pagenum)) { 03263 return $this->bMargin; 03264 } 03265 return $this->pagedim[$pagenum]['bm']; 03266 } 03267 03275 public function getScaleFactor() { 03276 return $this->k; 03277 } 03278 03289 public function SetMargins($left, $top, $right=-1, $keepmargins=false) { 03290 //Set left, top and right margins 03291 $this->lMargin = $left; 03292 $this->tMargin = $top; 03293 if ($right == -1) { 03294 $right = $left; 03295 } 03296 $this->rMargin = $right; 03297 if ($keepmargins) { 03298 // overwrite original values 03299 $this->original_lMargin = $this->lMargin; 03300 $this->original_rMargin = $this->rMargin; 03301 } 03302 } 03303 03311 public function SetLeftMargin($margin) { 03312 //Set left margin 03313 $this->lMargin = $margin; 03314 if (($this->page > 0) AND ($this->x < $margin)) { 03315 $this->x = $margin; 03316 } 03317 } 03318 03326 public function SetTopMargin($margin) { 03327 //Set top margin 03328 $this->tMargin = $margin; 03329 if (($this->page > 0) AND ($this->y < $margin)) { 03330 $this->y = $margin; 03331 } 03332 } 03333 03341 public function SetRightMargin($margin) { 03342 $this->rMargin = $margin; 03343 if (($this->page > 0) AND ($this->x > ($this->w - $margin))) { 03344 $this->x = $this->w - $margin; 03345 } 03346 } 03347 03355 public function SetCellPadding($pad) { 03356 if ($pad >= 0) { 03357 $this->cell_padding['L'] = $pad; 03358 $this->cell_padding['T'] = $pad; 03359 $this->cell_padding['R'] = $pad; 03360 $this->cell_padding['B'] = $pad; 03361 } 03362 } 03363 03374 public function setCellPaddings($left='', $top='', $right='', $bottom='') { 03375 if (($left !== '') AND ($left >= 0)) { 03376 $this->cell_padding['L'] = $left; 03377 } 03378 if (($top !== '') AND ($top >= 0)) { 03379 $this->cell_padding['T'] = $top; 03380 } 03381 if (($right !== '') AND ($right >= 0)) { 03382 $this->cell_padding['R'] = $right; 03383 } 03384 if (($bottom !== '') AND ($bottom >= 0)) { 03385 $this->cell_padding['B'] = $bottom; 03386 } 03387 } 03388 03396 public function getCellPaddings() { 03397 return $this->cell_padding; 03398 } 03399 03410 public function setCellMargins($left='', $top='', $right='', $bottom='') { 03411 if (($left !== '') AND ($left >= 0)) { 03412 $this->cell_margin['L'] = $left; 03413 } 03414 if (($top !== '') AND ($top >= 0)) { 03415 $this->cell_margin['T'] = $top; 03416 } 03417 if (($right !== '') AND ($right >= 0)) { 03418 $this->cell_margin['R'] = $right; 03419 } 03420 if (($bottom !== '') AND ($bottom >= 0)) { 03421 $this->cell_margin['B'] = $bottom; 03422 } 03423 } 03424 03432 public function getCellMargins() { 03433 return $this->cell_margin; 03434 } 03435 03443 protected function adjustCellPadding($brd=0) { 03444 if (empty($brd)) { 03445 return; 03446 } 03447 if (is_string($brd)) { 03448 // convert string to array 03449 $slen = strlen($brd); 03450 $newbrd = array(); 03451 for ($i = 0; $i < $slen; ++$i) { 03452 $newbrd[$brd{$i}] = true; 03453 } 03454 $brd = $newbrd; 03455 } elseif (($brd === 1) OR ($brd === true) OR (is_numeric($brd) AND (intval($brd) > 0))) { 03456 $brd = array('LRTB' => true); 03457 } 03458 if (!is_array($brd)) { 03459 return; 03460 } 03461 // store current cell padding 03462 $cp = $this->cell_padding; 03463 // select border mode 03464 if (isset($brd['mode'])) { 03465 $mode = $brd['mode']; 03466 unset($brd['mode']); 03467 } else { 03468 $mode = 'normal'; 03469 } 03470 // process borders 03471 foreach ($brd as $border => $style) { 03472 $line_width = $this->LineWidth; 03473 if (is_array($style) AND isset($style['width'])) { 03474 // get border width 03475 $line_width = $style['width']; 03476 } 03477 $adj = 0; // line width inside the cell 03478 switch ($mode) { 03479 case 'ext': { 03480 $adj = 0; 03481 break; 03482 } 03483 case 'int': { 03484 $adj = $line_width; 03485 break; 03486 } 03487 case 'normal': 03488 default: { 03489 $adj = ($line_width / 2); 03490 break; 03491 } 03492 } 03493 // correct internal cell padding if required to avoid overlap between text and lines 03494 if ((strpos($border,'T') !== false) AND ($this->cell_padding['T'] < $adj)) { 03495 $this->cell_padding['T'] = $adj; 03496 } 03497 if ((strpos($border,'R') !== false) AND ($this->cell_padding['R'] < $adj)) { 03498 $this->cell_padding['R'] = $adj; 03499 } 03500 if ((strpos($border,'B') !== false) AND ($this->cell_padding['B'] < $adj)) { 03501 $this->cell_padding['B'] = $adj; 03502 } 03503 if ((strpos($border,'L') !== false) AND ($this->cell_padding['L'] < $adj)) { 03504 $this->cell_padding['L'] = $adj; 03505 } 03506 } 03507 return array('T' => ($this->cell_padding['T'] - $cp['T']), 'R' => ($this->cell_padding['R'] - $cp['R']), 'B' => ($this->cell_padding['B'] - $cp['B']), 'L' => ($this->cell_padding['L'] - $cp['L'])); 03508 } 03509 03518 public function SetAutoPageBreak($auto, $margin=0) { 03519 $this->AutoPageBreak = $auto ? true : false; 03520 $this->bMargin = $margin; 03521 $this->PageBreakTrigger = $this->h - $margin; 03522 } 03523 03530 public function getAutoPageBreak() { 03531 return $this->AutoPageBreak; 03532 } 03533 03542 public function SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone') { 03543 if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) { 03544 $this->ZoomMode = $zoom; 03545 } else { 03546 $this->Error('Incorrect zoom display mode: '.$zoom); 03547 } 03548 switch ($layout) { 03549 case 'default': 03550 case 'single': 03551 case 'SinglePage': { 03552 $this->LayoutMode = 'SinglePage'; 03553 break; 03554 } 03555 case 'continuous': 03556 case 'OneColumn': { 03557 $this->LayoutMode = 'OneColumn'; 03558 break; 03559 } 03560 case 'two': 03561 case 'TwoColumnLeft': { 03562 $this->LayoutMode = 'TwoColumnLeft'; 03563 break; 03564 } 03565 case 'TwoColumnRight': { 03566 $this->LayoutMode = 'TwoColumnRight'; 03567 break; 03568 } 03569 case 'TwoPageLeft': { 03570 $this->LayoutMode = 'TwoPageLeft'; 03571 break; 03572 } 03573 case 'TwoPageRight': { 03574 $this->LayoutMode = 'TwoPageRight'; 03575 break; 03576 } 03577 default: { 03578 $this->LayoutMode = 'SinglePage'; 03579 } 03580 } 03581 switch ($mode) { 03582 case 'UseNone': { 03583 $this->PageMode = 'UseNone'; 03584 break; 03585 } 03586 case 'UseOutlines': { 03587 $this->PageMode = 'UseOutlines'; 03588 break; 03589 } 03590 case 'UseThumbs': { 03591 $this->PageMode = 'UseThumbs'; 03592 break; 03593 } 03594 case 'FullScreen': { 03595 $this->PageMode = 'FullScreen'; 03596 break; 03597 } 03598 case 'UseOC': { 03599 $this->PageMode = 'UseOC'; 03600 break; 03601 } 03602 case '': { 03603 $this->PageMode = 'UseAttachments'; 03604 break; 03605 } 03606 default: { 03607 $this->PageMode = 'UseNone'; 03608 } 03609 } 03610 } 03611 03619 public function SetCompression($compress=true) { 03620 if (function_exists('gzcompress')) { 03621 $this->compress = $compress ? true : false; 03622 } else { 03623 $this->compress = false; 03624 } 03625 } 03626 03633 public function setSRGBmode($mode=false) { 03634 $this->force_srgb = $mode ? true : false; 03635 } 03636 03644 public function SetDocInfoUnicode($unicode=true) { 03645 $this->docinfounicode = $unicode ? true : false; 03646 } 03647 03655 public function SetTitle($title) { 03656 $this->title = $title; 03657 } 03658 03666 public function SetSubject($subject) { 03667 $this->subject = $subject; 03668 } 03669 03677 public function SetAuthor($author) { 03678 $this->author = $author; 03679 } 03680 03688 public function SetKeywords($keywords) { 03689 $this->keywords = $keywords; 03690 } 03691 03699 public function SetCreator($creator) { 03700 $this->creator = $creator; 03701 } 03702 03710 public function Error($msg) { 03711 // unset all class variables 03712 $this->_destroy(true); 03713 // exit program and print error 03714 die('<strong>TCPDF ERROR: </strong>'.$msg); 03715 } 03716 03725 public function Open() { 03726 $this->state = 1; 03727 } 03728 03737 public function Close() { 03738 if ($this->state == 3) { 03739 return; 03740 } 03741 if ($this->page == 0) { 03742 $this->AddPage(); 03743 } 03744 $this->endLayer(); 03745 // save current graphic settings 03746 $gvars = $this->getGraphicVars(); 03747 $this->setEqualColumns(); 03748 $this->lastpage(true); 03749 $this->SetAutoPageBreak(false); 03750 $this->x = 0; 03751 $this->y = $this->h - (1 / $this->k); 03752 $this->lMargin = 0; 03753 $this->_out('q'); 03754 $this->SetFont('helvetica', '', 1); 03755 $this->setTextRenderingMode(0, false, false); 03756 $msg = "\x50\x6f\x77\x65\x72\x65\x64\x20\x62\x79\x20\x54\x43\x50\x44\x46\x20\x28\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67\x29"; 03757 $lnk = "\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67"; 03758 $this->Cell(0, 0, $msg, 0, 0, 'L', 0, $lnk, 0, false, 'D', 'B'); 03759 $this->_out('Q'); 03760 // restore graphic settings 03761 $this->setGraphicVars($gvars); 03762 // close page 03763 $this->endPage(); 03764 // close document 03765 $this->_enddoc(); 03766 // unset all class variables (except critical ones) 03767 $this->_destroy(false); 03768 } 03769 03778 public function setPage($pnum, $resetmargins=false) { 03779 if (($pnum == $this->page) AND ($this->state == 2)) { 03780 return; 03781 } 03782 if (($pnum > 0) AND ($pnum <= $this->numpages)) { 03783 $this->state = 2; 03784 // save current graphic settings 03785 //$gvars = $this->getGraphicVars(); 03786 $oldpage = $this->page; 03787 $this->page = $pnum; 03788 $this->wPt = $this->pagedim[$this->page]['w']; 03789 $this->hPt = $this->pagedim[$this->page]['h']; 03790 $this->w = $this->pagedim[$this->page]['wk']; 03791 $this->h = $this->pagedim[$this->page]['hk']; 03792 $this->tMargin = $this->pagedim[$this->page]['tm']; 03793 $this->bMargin = $this->pagedim[$this->page]['bm']; 03794 $this->original_lMargin = $this->pagedim[$this->page]['olm']; 03795 $this->original_rMargin = $this->pagedim[$this->page]['orm']; 03796 $this->AutoPageBreak = $this->pagedim[$this->page]['pb']; 03797 $this->CurOrientation = $this->pagedim[$this->page]['or']; 03798 $this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin); 03799 // restore graphic settings 03800 //$this->setGraphicVars($gvars); 03801 if ($resetmargins) { 03802 $this->lMargin = $this->pagedim[$this->page]['olm']; 03803 $this->rMargin = $this->pagedim[$this->page]['orm']; 03804 $this->SetY($this->tMargin); 03805 } else { 03806 // account for booklet mode 03807 if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) { 03808 $deltam = $this->pagedim[$this->page]['olm'] - $this->pagedim[$this->page]['orm']; 03809 $this->lMargin += $deltam; 03810 $this->rMargin -= $deltam; 03811 } 03812 } 03813 } else { 03814 $this->Error('Wrong page number on setPage() function: '.$pnum); 03815 } 03816 } 03817 03825 public function lastPage($resetmargins=false) { 03826 $this->setPage($this->getNumPages(), $resetmargins); 03827 } 03828 03836 public function getPage() { 03837 return $this->page; 03838 } 03839 03847 public function getNumPages() { 03848 return $this->numpages; 03849 } 03850 03860 public function addTOCPage($orientation='', $format='', $keepmargins=false) { 03861 $this->AddPage($orientation, $format, $keepmargins, true); 03862 } 03863 03870 public function endTOCPage() { 03871 $this->endPage(true); 03872 } 03873 03885 public function AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false) { 03886 if ($this->inxobj) { 03887 // we are inside an XObject template 03888 return; 03889 } 03890 if (!isset($this->original_lMargin) OR $keepmargins) { 03891 $this->original_lMargin = $this->lMargin; 03892 } 03893 if (!isset($this->original_rMargin) OR $keepmargins) { 03894 $this->original_rMargin = $this->rMargin; 03895 } 03896 // terminate previous page 03897 $this->endPage(); 03898 // start new page 03899 $this->startPage($orientation, $format, $tocpage); 03900 } 03901 03909 public function endPage($tocpage=false) { 03910 // check if page is already closed 03911 if (($this->page == 0) OR ($this->numpages > $this->page) OR (!$this->pageopen[$this->page])) { 03912 return; 03913 } 03914 // print page footer 03915 $this->setFooter(); 03916 // close page 03917 $this->_endpage(); 03918 // mark page as closed 03919 $this->pageopen[$this->page] = false; 03920 if ($tocpage) { 03921 $this->tocpage = false; 03922 } 03923 } 03924 03935 public function startPage($orientation='', $format='', $tocpage=false) { 03936 if ($tocpage) { 03937 $this->tocpage = true; 03938 } 03939 // move page numbers of documents to be attached 03940 if ($this->tocpage) { 03941 // move reference to unexistent pages (used for page attachments) 03942 // adjust outlines 03943 $tmpoutlines = $this->outlines; 03944 foreach ($tmpoutlines as $key => $outline) { 03945 if ($outline['p'] > $this->numpages) { 03946 $this->outlines[$key]['p'] = ($outline['p'] + 1); 03947 } 03948 } 03949 // adjust dests 03950 $tmpdests = $this->dests; 03951 foreach ($tmpdests as $key => $dest) { 03952 if ($dest['p'] > $this->numpages) { 03953 $this->dests[$key]['p'] = ($dest['p'] + 1); 03954 } 03955 } 03956 // adjust links 03957 $tmplinks = $this->links; 03958 foreach ($tmplinks as $key => $link) { 03959 if ($link[0] > $this->numpages) { 03960 $this->links[$key][0] = ($link[0] + 1); 03961 } 03962 } 03963 } 03964 if ($this->numpages > $this->page) { 03965 // this page has been already added 03966 $this->setPage($this->page + 1); 03967 $this->SetY($this->tMargin); 03968 return; 03969 } 03970 // start a new page 03971 if ($this->state == 0) { 03972 $this->Open(); 03973 } 03974 ++$this->numpages; 03975 $this->swapMargins($this->booklet); 03976 // save current graphic settings 03977 $gvars = $this->getGraphicVars(); 03978 // start new page 03979 $this->_beginpage($orientation, $format); 03980 // mark page as open 03981 $this->pageopen[$this->page] = true; 03982 // restore graphic settings 03983 $this->setGraphicVars($gvars); 03984 // mark this point 03985 $this->setPageMark(); 03986 // print page header 03987 $this->setHeader(); 03988 // restore graphic settings 03989 $this->setGraphicVars($gvars); 03990 // mark this point 03991 $this->setPageMark(); 03992 // print table header (if any) 03993 $this->setTableHeader(); 03994 // set mark for empty page check 03995 $this->emptypagemrk[$this->page]= $this->pagelen[$this->page]; 03996 } 03997 04006 public function setPageMark() { 04007 $this->intmrk[$this->page] = $this->pagelen[$this->page]; 04008 $this->bordermrk[$this->page] = $this->intmrk[$this->page]; 04009 $this->setContentMark(); 04010 } 04011 04019 protected function setContentMark($page=0) { 04020 if ($page <= 0) { 04021 $page = $this->page; 04022 } 04023 if (isset($this->footerlen[$page])) { 04024 $this->cntmrk[$page] = $this->pagelen[$page] - $this->footerlen[$page]; 04025 } else { 04026 $this->cntmrk[$page] = $this->pagelen[$page]; 04027 } 04028 } 04029 04038 public function setHeaderData($ln='', $lw=0, $ht='', $hs='') { 04039 $this->header_logo = $ln; 04040 $this->header_logo_width = $lw; 04041 $this->header_title = $ht; 04042 $this->header_string = $hs; 04043 } 04044 04052 public function getHeaderData() { 04053 $ret = array(); 04054 $ret['logo'] = $this->header_logo; 04055 $ret['logo_width'] = $this->header_logo_width; 04056 $ret['title'] = $this->header_title; 04057 $ret['string'] = $this->header_string; 04058 return $ret; 04059 } 04060 04067 public function setHeaderMargin($hm=10) { 04068 $this->header_margin = $hm; 04069 } 04070 04077 public function getHeaderMargin() { 04078 return $this->header_margin; 04079 } 04080 04087 public function setFooterMargin($fm=10) { 04088 $this->footer_margin = $fm; 04089 } 04090 04097 public function getFooterMargin() { 04098 return $this->footer_margin; 04099 } 04105 public function setPrintHeader($val=true) { 04106 $this->print_header = $val ? true : false; 04107 } 04108 04114 public function setPrintFooter($val=true) { 04115 $this->print_footer = $val ? true : false; 04116 } 04117 04123 public function getImageRBX() { 04124 return $this->img_rb_x; 04125 } 04126 04132 public function getImageRBY() { 04133 return $this->img_rb_y; 04134 } 04135 04140 public function resetHeaderTemplate() { 04141 $this->header_xobjid = -1; 04142 } 04143 04149 public function setHeaderTemplateAutoreset($val=true) { 04150 $this->header_xobj_autoreset = $val ? true : false; 04151 } 04152 04158 public function Header() { 04159 if ($this->header_xobjid < 0) { 04160 // start a new XObject Template 04161 $this->header_xobjid = $this->startTemplate($this->w, $this->tMargin); 04162 $headerfont = $this->getHeaderFont(); 04163 $headerdata = $this->getHeaderData(); 04164 $this->y = $this->header_margin; 04165 if ($this->rtl) { 04166 $this->x = $this->w - $this->original_rMargin; 04167 } else { 04168 $this->x = $this->original_lMargin; 04169 } 04170 if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) { 04171 $imgtype = $this->getImageFileType(K_PATH_IMAGES.$headerdata['logo']); 04172 if (($imgtype == 'eps') OR ($imgtype == 'ai')) { 04173 $this->ImageEps(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']); 04174 } elseif ($imgtype == 'svg') { 04175 $this->ImageSVG(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']); 04176 } else { 04177 $this->Image(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']); 04178 } 04179 $imgy = $this->getImageRBY(); 04180 } else { 04181 $imgy = $this->y; 04182 } 04183 $cell_height = round(($this->cell_height_ratio * $headerfont[2]) / $this->k, 2); 04184 // set starting margin for text data cell 04185 if ($this->getRTL()) { 04186 $header_x = $this->original_rMargin + ($headerdata['logo_width'] * 1.1); 04187 } else { 04188 $header_x = $this->original_lMargin + ($headerdata['logo_width'] * 1.1); 04189 } 04190 $cw = $this->w - $this->original_lMargin - $this->original_rMargin - ($headerdata['logo_width'] * 1.1); 04191 $this->SetTextColor(0, 0, 0); 04192 // header title 04193 $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1); 04194 $this->SetX($header_x); 04195 $this->Cell($cw, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0); 04196 // header string 04197 $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]); 04198 $this->SetX($header_x); 04199 $this->MultiCell($cw, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false, true, 0, 'T', false); 04200 // print an ending header line 04201 $this->SetLineStyle(array('width' => 0.85 / $this->k, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))); 04202 $this->SetY((2.835 / $this->k) + max($imgy, $this->y)); 04203 if ($this->rtl) { 04204 $this->SetX($this->original_rMargin); 04205 } else { 04206 $this->SetX($this->original_lMargin); 04207 } 04208 $this->Cell(($this->w - $this->original_lMargin - $this->original_rMargin), 0, '', 'T', 0, 'C'); 04209 $this->endTemplate(); 04210 } 04211 // print header template 04212 $x = 0; 04213 $dx = 0; 04214 if ($this->booklet AND (($this->page % 2) == 0)) { 04215 // adjust margins for booklet mode 04216 $dx = ($this->original_lMargin - $this->original_rMargin); 04217 } 04218 if ($this->rtl) { 04219 $x = $this->w + $dx; 04220 } else { 04221 $x = 0 + $dx; 04222 } 04223 $this->printTemplate($this->header_xobjid, $x, 0, 0, 0, '', '', false); 04224 if ($this->header_xobj_autoreset) { 04225 // reset header xobject template at each page 04226 $this->header_xobjid = -1; 04227 } 04228 } 04229 04235 public function Footer() { 04236 $cur_y = $this->y; 04237 $this->SetTextColor(0, 0, 0); 04238 //set style for cell border 04239 $line_width = 0.85 / $this->k; 04240 $this->SetLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))); 04241 //print document barcode 04242 $barcode = $this->getBarcode(); 04243 if (!empty($barcode)) { 04244 $this->Ln($line_width); 04245 $barcode_width = round(($this->w - $this->original_lMargin - $this->original_rMargin) / 3); 04246 $style = array( 04247 'position' => $this->rtl?'R':'L', 04248 'align' => $this->rtl?'R':'L', 04249 'stretch' => false, 04250 'fitwidth' => true, 04251 'cellfitalign' => '', 04252 'border' => false, 04253 'padding' => 0, 04254 'fgcolor' => array(0,0,0), 04255 'bgcolor' => false, 04256 'text' => false 04257 ); 04258 $this->write1DBarcode($barcode, 'C128', '', $cur_y + $line_width, '', (($this->footer_margin / 3) - $line_width), 0.3, $style, ''); 04259 } 04260 if (empty($this->pagegroups)) { 04261 $pagenumtxt = $this->l['w_page'].' '.$this->getAliasNumPage().' / '.$this->getAliasNbPages(); 04262 } else { 04263 $pagenumtxt = $this->l['w_page'].' '.$this->getPageNumGroupAlias().' / '.$this->getPageGroupAlias(); 04264 } 04265 $this->SetY($cur_y); 04266 //Print page number 04267 if ($this->getRTL()) { 04268 $this->SetX($this->original_rMargin); 04269 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L'); 04270 } else { 04271 $this->SetX($this->original_lMargin); 04272 $this->Cell(0, 0, $this->getAliasRightShift().$pagenumtxt, 'T', 0, 'R'); 04273 } 04274 } 04275 04281 protected function setHeader() { 04282 if (!$this->print_header) { 04283 return; 04284 } 04285 $this->InHeader = true; 04286 $this->setGraphicVars($this->default_graphic_vars); 04287 $temp_thead = $this->thead; 04288 $temp_theadMargins = $this->theadMargins; 04289 $lasth = $this->lasth; 04290 $this->_out('q'); 04291 $this->rMargin = $this->original_rMargin; 04292 $this->lMargin = $this->original_lMargin; 04293 $this->SetCellPadding(0); 04294 //set current position 04295 if ($this->rtl) { 04296 $this->SetXY($this->original_rMargin, $this->header_margin); 04297 } else { 04298 $this->SetXY($this->original_lMargin, $this->header_margin); 04299 } 04300 $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]); 04301 $this->Header(); 04302 //restore position 04303 if ($this->rtl) { 04304 $this->SetXY($this->original_rMargin, $this->tMargin); 04305 } else { 04306 $this->SetXY($this->original_lMargin, $this->tMargin); 04307 } 04308 $this->_out('Q'); 04309 $this->lasth = $lasth; 04310 $this->thead = $temp_thead; 04311 $this->theadMargins = $temp_theadMargins; 04312 $this->newline = false; 04313 $this->InHeader = false; 04314 } 04315 04321 protected function setFooter() { 04322 //Page footer 04323 $this->InFooter = true; 04324 // save current graphic settings 04325 $gvars = $this->getGraphicVars(); 04326 // mark this point 04327 $this->footerpos[$this->page] = $this->pagelen[$this->page]; 04328 $this->_out("\n"); 04329 if ($this->print_footer) { 04330 $this->setGraphicVars($this->default_graphic_vars); 04331 $this->current_column = 0; 04332 $this->num_columns = 1; 04333 $temp_thead = $this->thead; 04334 $temp_theadMargins = $this->theadMargins; 04335 $lasth = $this->lasth; 04336 $this->_out('q'); 04337 $this->rMargin = $this->original_rMargin; 04338 $this->lMargin = $this->original_lMargin; 04339 $this->SetCellPadding(0); 04340 //set current position 04341 $footer_y = $this->h - $this->footer_margin; 04342 if ($this->rtl) { 04343 $this->SetXY($this->original_rMargin, $footer_y); 04344 } else { 04345 $this->SetXY($this->original_lMargin, $footer_y); 04346 } 04347 $this->SetFont($this->footer_font[0], $this->footer_font[1], $this->footer_font[2]); 04348 $this->Footer(); 04349 //restore position 04350 if ($this->rtl) { 04351 $this->SetXY($this->original_rMargin, $this->tMargin); 04352 } else { 04353 $this->SetXY($this->original_lMargin, $this->tMargin); 04354 } 04355 $this->_out('Q'); 04356 $this->lasth = $lasth; 04357 $this->thead = $temp_thead; 04358 $this->theadMargins = $temp_theadMargins; 04359 } 04360 // restore graphic settings 04361 $this->setGraphicVars($gvars); 04362 $this->current_column = $gvars['current_column']; 04363 $this->num_columns = $gvars['num_columns']; 04364 // calculate footer length 04365 $this->footerlen[$this->page] = $this->pagelen[$this->page] - $this->footerpos[$this->page] + 1; 04366 $this->InFooter = false; 04367 } 04368 04375 protected function inPageBody() { 04376 return (($this->InHeader === false) AND ($this->InFooter === false)); 04377 } 04378 04384 protected function setTableHeader() { 04385 if ($this->num_columns > 1) { 04386 // multi column mode 04387 return; 04388 } 04389 if (isset($this->theadMargins['top'])) { 04390 // restore the original top-margin 04391 $this->tMargin = $this->theadMargins['top']; 04392 $this->pagedim[$this->page]['tm'] = $this->tMargin; 04393 $this->y = $this->tMargin; 04394 } 04395 if (!$this->empty_string($this->thead) AND (!$this->inthead)) { 04396 // set margins 04397 $prev_lMargin = $this->lMargin; 04398 $prev_rMargin = $this->rMargin; 04399 $prev_cell_padding = $this->cell_padding; 04400 $this->lMargin = $this->theadMargins['lmargin'] + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$this->theadMargins['page']]['olm']); 04401 $this->rMargin = $this->theadMargins['rmargin'] + ($this->pagedim[$this->page]['orm'] - $this->pagedim[$this->theadMargins['page']]['orm']); 04402 $this->cell_padding = $this->theadMargins['cell_padding']; 04403 if ($this->rtl) { 04404 $this->x = $this->w - $this->rMargin; 04405 } else { 04406 $this->x = $this->lMargin; 04407 } 04408 // account for special "cell" mode 04409 if ($this->theadMargins['cell']) { 04410 if ($this->rtl) { 04411 $this->x -= $this->cell_padding['R']; 04412 } else { 04413 $this->x += $this->cell_padding['L']; 04414 } 04415 } 04416 // print table header 04417 $this->writeHTML($this->thead, false, false, false, false, ''); 04418 // set new top margin to skip the table headers 04419 if (!isset($this->theadMargins['top'])) { 04420 $this->theadMargins['top'] = $this->tMargin; 04421 } 04422 // store end of header position 04423 if (!isset($this->columns[0]['th'])) { 04424 $this->columns[0]['th'] = array(); 04425 } 04426 $this->columns[0]['th']['\''.$this->page.'\''] = $this->y; 04427 $this->tMargin = $this->y; 04428 $this->pagedim[$this->page]['tm'] = $this->tMargin; 04429 $this->lasth = 0; 04430 $this->lMargin = $prev_lMargin; 04431 $this->rMargin = $prev_rMargin; 04432 $this->cell_padding = $prev_cell_padding; 04433 } 04434 } 04435 04443 public function PageNo() { 04444 return $this->page; 04445 } 04446 04460 public function AddSpotColor($name, $c, $m, $y, $k) { 04461 if (!isset($this->spot_colors[$name])) { 04462 $i = (1 + count($this->spot_colors)); 04463 $this->spot_colors[$name] = array('i' => $i, 'c' => $c, 'm' => $m, 'y' => $y, 'k' => $k); 04464 } 04465 } 04466 04474 public function getSpotColor($name) { 04475 if (isset($this->spot_colors[$name])) { 04476 return $this->spot_colors[$name]; 04477 } 04478 $color = preg_replace('/[\s]*/', '', $name); // remove extra spaces 04479 $color = strtolower($color); 04480 if (isset($this->spotcolor[$color])) { 04481 $this->AddSpotColor($this->spotcolor[$color][4], $this->spotcolor[$color][0], $this->spotcolor[$color][1], $this->spotcolor[$color][2], $this->spotcolor[$color][3]); 04482 return $this->spot_colors[$name]; 04483 } 04484 return false; 04485 } 04486 04496 public function setSpotColor($type, $name, $tint=100) { 04497 $spotcolor = $this->getSpotColor($name); 04498 if ($spotcolor === false) { 04499 $this->Error('Undefined spot color: '.$name.', you must add it on the spotcolors.php file.'); 04500 } 04501 $tint = (max(0, min(100, $tint)) / 100); 04502 $intcolor = array('C' => $spotcolor['c'], 'M' => $spotcolor['m'], 'Y' => $spotcolor['y'], 'K' => $spotcolor['k'], 'name' => $spotcolor['i']); 04503 $pdfcolor = sprintf('/CS%d ', $this->spot_colors[$name]['i']); 04504 switch ($type) { 04505 case 'draw': { 04506 $pdfcolor .= sprintf('CS %.3F SCN', $tint); 04507 $this->DrawColor = $pdfcolor; 04508 $this->strokecolor = $intcolor; 04509 break; 04510 } 04511 case 'fill': { 04512 $pdfcolor .= sprintf('cs %.3F scn', $tint); 04513 $this->FillColor = $pdfcolor; 04514 $this->bgcolor = $intcolor; 04515 break; 04516 } 04517 case 'text': { 04518 $pdfcolor .= sprintf('cs %.3F scn', $tint); 04519 $this->TextColor = $pdfcolor; 04520 $this->fgcolor = $intcolor; 04521 break; 04522 } 04523 } 04524 $this->ColorFlag = ($this->FillColor != $this->TextColor); 04525 if ($this->page > 0) { 04526 $this->_out($pdfcolor); 04527 } 04528 if ($this->inxobj) { 04529 // we are inside an XObject template 04530 $this->xobjects[$this->xobjid]['spot_colors'][$name] = $this->spot_colors[$name]; 04531 } 04532 return $pdfcolor; 04533 } 04534 04543 public function SetDrawSpotColor($name, $tint=100) { 04544 $this->setSpotColor('draw', $name, $tint); 04545 } 04546 04555 public function SetFillSpotColor($name, $tint=100) { 04556 $this->setSpotColor('fill', $name, $tint); 04557 } 04558 04567 public function SetTextSpotColor($name, $tint=100) { 04568 $this->setSpotColor('text', $name, $tint); 04569 } 04570 04582 public function setColorArray($type, $color, $ret=false) { 04583 if (is_array($color)) { 04584 $color = array_values($color); 04585 // component: grey, RGB red or CMYK cyan 04586 $r = isset($color[0]) ? $color[0] : -1; 04587 // component: RGB green or CMYK magenta 04588 $g = isset($color[1]) ? $color[1] : -1; 04589 // component: RGB blue or CMYK yellow 04590 $b = isset($color[2]) ? $color[2] : -1; 04591 // component: CMYK black 04592 $k = isset($color[3]) ? $color[3] : -1; 04593 // spot color name 04594 $name = isset($color[4]) ? $color[4] : ''; 04595 if ($r >= 0) { 04596 return $this->setColor($type, $r, $g, $b, $k, $ret, $name); 04597 } 04598 } 04599 return ''; 04600 } 04601 04613 public function SetDrawColorArray($color, $ret=false) { 04614 return $this->setColorArray('draw', $color, $ret); 04615 } 04616 04627 public function SetFillColorArray($color, $ret=false) { 04628 return $this->setColorArray('fill', $color, $ret); 04629 } 04630 04640 public function SetTextColorArray($color, $ret=false) { 04641 return $this->setColorArray('text', $color, $ret); 04642 } 04643 04657 public function setColor($type, $col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') { 04658 // set default values 04659 if (!is_numeric($col1)) { 04660 $col1 = 0; 04661 } 04662 if (!is_numeric($col2)) { 04663 $col2 = -1; 04664 } 04665 if (!is_numeric($col3)) { 04666 $col3 = -1; 04667 } 04668 if (!is_numeric($col4)) { 04669 $col4 = -1; 04670 } 04671 // set color by case 04672 $suffix = ''; 04673 if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) { 04674 // Grey scale 04675 $col1 = max(0, min(255, $col1)); 04676 $intcolor = array('G' => $col1); 04677 $pdfcolor = sprintf('%.3F ', ($col1 / 255)); 04678 $suffix = 'g'; 04679 } elseif ($col4 == -1) { 04680 // RGB 04681 $col1 = max(0, min(255, $col1)); 04682 $col2 = max(0, min(255, $col2)); 04683 $col3 = max(0, min(255, $col3)); 04684 $intcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3); 04685 $pdfcolor = sprintf('%.3F %.3F %.3F ', ($col1 / 255), ($col2 / 255), ($col3 / 255)); 04686 $suffix = 'rg'; 04687 } else { 04688 $col1 = max(0, min(100, $col1)); 04689 $col2 = max(0, min(100, $col2)); 04690 $col3 = max(0, min(100, $col3)); 04691 $col4 = max(0, min(100, $col4)); 04692 if (empty($name)) { 04693 // CMYK 04694 $intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4); 04695 $pdfcolor = sprintf('%.3F %.3F %.3F %.3F ', ($col1 / 100), ($col2 / 100), ($col3 / 100), ($col4 / 100)); 04696 $suffix = 'k'; 04697 } else { 04698 // SPOT COLOR 04699 $intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4, 'name' => $name); 04700 $this->AddSpotColor($name, $col1, $col2, $col3, $col4); 04701 $pdfcolor = $this->setSpotColor($type, $name, 100); 04702 } 04703 } 04704 switch ($type) { 04705 case 'draw': { 04706 $pdfcolor .= strtoupper($suffix); 04707 $this->DrawColor = $pdfcolor; 04708 $this->strokecolor = $intcolor; 04709 break; 04710 } 04711 case 'fill': { 04712 $pdfcolor .= $suffix; 04713 $this->FillColor = $pdfcolor; 04714 $this->bgcolor = $intcolor; 04715 break; 04716 } 04717 case 'text': { 04718 $pdfcolor .= $suffix; 04719 $this->TextColor = $pdfcolor; 04720 $this->fgcolor = $intcolor; 04721 break; 04722 } 04723 } 04724 $this->ColorFlag = ($this->FillColor != $this->TextColor); 04725 if (($type != 'text') AND ($this->page > 0)) { 04726 if (!$ret) { 04727 $this->_out($pdfcolor); 04728 } 04729 return $pdfcolor; 04730 } 04731 return ''; 04732 } 04733 04747 public function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') { 04748 return $this->setColor('draw', $col1, $col2, $col3, $col4, $ret, $name); 04749 } 04750 04764 public function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') { 04765 return $this->setColor('fill', $col1, $col2, $col3, $col4, $ret, $name); 04766 } 04767 04781 public function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') { 04782 return $this->setColor('text', $col1, $col2, $col3, $col4, $ret, $name); 04783 } 04784 04797 public function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) { 04798 return $this->GetArrStringWidth($this->utf8Bidi($this->UTF8StringToArray($s), $s, $this->tmprtl), $fontname, $fontstyle, $fontsize, $getarray); 04799 } 04800 04813 public function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) { 04814 // store current values 04815 if (!$this->empty_string($fontname)) { 04816 $prev_FontFamily = $this->FontFamily; 04817 $prev_FontStyle = $this->FontStyle; 04818 $prev_FontSizePt = $this->FontSizePt; 04819 $this->SetFont($fontname, $fontstyle, $fontsize, '', 'default', false); 04820 } 04821 // convert UTF-8 array to Latin1 if required 04822 $sa = $this->UTF8ArrToLatin1($sa); 04823 $w = 0; // total width 04824 $wa = array(); // array of characters widths 04825 foreach ($sa as $ck => $char) { 04826 // character width 04827 $cw = $this->GetCharWidth($char, isset($sa[($ck + 1)])); 04828 $wa[] = $cw; 04829 $w += $cw; 04830 } 04831 // restore previous values 04832 if (!$this->empty_string($fontname)) { 04833 $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt, '', 'default', false); 04834 } 04835 if ($getarray) { 04836 return $wa; 04837 } 04838 return $w; 04839 } 04840 04850 public function GetCharWidth($char, $notlast=true) { 04851 // get raw width 04852 $chw = $this->getRawCharWidth($char); 04853 if (($this->font_spacing != 0) AND $notlast) { 04854 // increase/decrease font spacing 04855 $chw += $this->font_spacing; 04856 } 04857 if ($this->font_stretching != 100) { 04858 // fixed stretching mode 04859 $chw *= ($this->font_stretching / 100); 04860 } 04861 return $chw; 04862 } 04863 04872 public function getRawCharWidth($char) { 04873 if ($char == 173) { 04874 // SHY character will not be printed 04875 return (0); 04876 } 04877 if (isset($this->CurrentFont['cw'][$char])) { 04878 $w = $this->CurrentFont['cw'][$char]; 04879 } elseif (isset($this->CurrentFont['dw'])) { 04880 // default width 04881 $w = $this->CurrentFont['dw']; 04882 } elseif (isset($this->CurrentFont['cw'][32])) { 04883 // default width 04884 $w = $this->CurrentFont['cw'][32]; 04885 } else { 04886 $w = 600; 04887 } 04888 return ($w * $this->FontSize / 1000); 04889 } 04890 04898 public function GetNumChars($s) { 04899 if ($this->isUnicodeFont()) { 04900 return count($this->UTF8StringToArray($s)); 04901 } 04902 return strlen($s); 04903 } 04904 04910 protected function getFontsList() { 04911 $fontsdir = opendir($this->_getfontpath()); 04912 while (($file = readdir($fontsdir)) !== false) { 04913 if (substr($file, -4) == '.php') { 04914 array_push($this->fontlist, strtolower(basename($file, '.php'))); 04915 } 04916 } 04917 closedir($fontsdir); 04918 } 04919 04933 public function AddFont($family, $style='', $fontfile='', $subset='default') { 04934 if ($subset === 'default') { 04935 $subset = $this->font_subsetting; 04936 } 04937 if ($this->pdfa_mode) { 04938 $subset = false; 04939 } 04940 if ($this->empty_string($family)) { 04941 if (!$this->empty_string($this->FontFamily)) { 04942 $family = $this->FontFamily; 04943 } else { 04944 $this->Error('Empty font family'); 04945 } 04946 } 04947 // move embedded styles on $style 04948 if (substr($family, -1) == 'I') { 04949 $style .= 'I'; 04950 $family = substr($family, 0, -1); 04951 } 04952 if (substr($family, -1) == 'B') { 04953 $style .= 'B'; 04954 $family = substr($family, 0, -1); 04955 } 04956 // normalize family name 04957 $family = strtolower($family); 04958 if ((!$this->isunicode) AND ($family == 'arial')) { 04959 $family = 'helvetica'; 04960 } 04961 if (($family == 'symbol') OR ($family == 'zapfdingbats')) { 04962 $style = ''; 04963 } 04964 if ($this->pdfa_mode AND (isset($this->CoreFonts[$family]))) { 04965 // all fonts must be embedded 04966 $family = 'pdfa'.$family; 04967 } 04968 $tempstyle = strtoupper($style); 04969 $style = ''; 04970 // underline 04971 if (strpos($tempstyle, 'U') !== false) { 04972 $this->underline = true; 04973 } else { 04974 $this->underline = false; 04975 } 04976 // line-through (deleted) 04977 if (strpos($tempstyle, 'D') !== false) { 04978 $this->linethrough = true; 04979 } else { 04980 $this->linethrough = false; 04981 } 04982 // overline 04983 if (strpos($tempstyle, 'O') !== false) { 04984 $this->overline = true; 04985 } else { 04986 $this->overline = false; 04987 } 04988 // bold 04989 if (strpos($tempstyle, 'B') !== false) { 04990 $style .= 'B'; 04991 } 04992 // oblique 04993 if (strpos($tempstyle, 'I') !== false) { 04994 $style .= 'I'; 04995 } 04996 $bistyle = $style; 04997 $fontkey = $family.$style; 04998 $font_style = $style.($this->underline ? 'U' : '').($this->linethrough ? 'D' : '').($this->overline ? 'O' : ''); 04999 $fontdata = array('fontkey' => $fontkey, 'family' => $family, 'style' => $font_style); 05000 // check if the font has been already added 05001 $fb = $this->getFontBuffer($fontkey); 05002 if ($fb !== false) { 05003 if ($this->inxobj) { 05004 // we are inside an XObject template 05005 $this->xobjects[$this->xobjid]['fonts'][$fontkey] = $fb['i']; 05006 } 05007 return $fontdata; 05008 } 05009 if (isset($type)) { 05010 unset($type); 05011 } 05012 if (isset($cw)) { 05013 unset($cw); 05014 } 05015 // get specified font directory (if any) 05016 $fontdir = false; 05017 if (!$this->empty_string($fontfile)) { 05018 $fontdir = dirname($fontfile); 05019 if ($this->empty_string($fontdir) OR ($fontdir == '.')) { 05020 $fontdir = ''; 05021 } else { 05022 $fontdir .= '/'; 05023 } 05024 } 05025 $missing_style = false; // true when the font style variation is missing 05026 // search and include font file 05027 if ($this->empty_string($fontfile) OR (!file_exists($fontfile))) { 05028 // build a standard filenames for specified font 05029 $tmp_fontfile = str_replace(' ', '', $family).strtolower($style).'.php'; 05030 // search files on various directories 05031 if (($fontdir !== false) AND file_exists($fontdir.$tmp_fontfile)) { 05032 $fontfile = $fontdir.$tmp_fontfile; 05033 } elseif (file_exists($this->_getfontpath().$tmp_fontfile)) { 05034 $fontfile = $this->_getfontpath().$tmp_fontfile; 05035 } elseif (file_exists($tmp_fontfile)) { 05036 $fontfile = $tmp_fontfile; 05037 } elseif (!$this->empty_string($style)) { 05038 $missing_style = true; 05039 // try to remove the style part 05040 $tmp_fontfile = str_replace(' ', '', $family).'.php'; 05041 if (($fontdir !== false) AND file_exists($fontdir.$tmp_fontfile)) { 05042 $fontfile = $fontdir.$tmp_fontfile; 05043 } elseif (file_exists($this->_getfontpath().$tmp_fontfile)) { 05044 $fontfile = $this->_getfontpath().$tmp_fontfile; 05045 } else { 05046 $fontfile = $tmp_fontfile; 05047 } 05048 } 05049 } 05050 // include font file 05051 if (file_exists($fontfile)) { 05052 include($fontfile); 05053 } else { 05054 $this->Error('Could not include font definition file: '.$family.''); 05055 } 05056 // check font parameters 05057 if ((!isset($type)) OR (!isset($cw))) { 05058 $this->Error('The font definition file has a bad format: '.$fontfile.''); 05059 } 05060 // SET default parameters 05061 if (!isset($file) OR $this->empty_string($file)) { 05062 $file = ''; 05063 } 05064 if (!isset($enc) OR $this->empty_string($enc)) { 05065 $enc = ''; 05066 } 05067 if (!isset($cidinfo) OR $this->empty_string($cidinfo)) { 05068 $cidinfo = array('Registry'=>'Adobe', 'Ordering'=>'Identity', 'Supplement'=>0); 05069 $cidinfo['uni2cid'] = array(); 05070 } 05071 if (!isset($ctg) OR $this->empty_string($ctg)) { 05072 $ctg = ''; 05073 } 05074 if (!isset($desc) OR $this->empty_string($desc)) { 05075 $desc = array(); 05076 } 05077 if (!isset($up) OR $this->empty_string($up)) { 05078 $up = -100; 05079 } 05080 if (!isset($ut) OR $this->empty_string($ut)) { 05081 $ut = 50; 05082 } 05083 if (!isset($cw) OR $this->empty_string($cw)) { 05084 $cw = array(); 05085 } 05086 if (!isset($dw) OR $this->empty_string($dw)) { 05087 // set default width 05088 if (isset($desc['MissingWidth']) AND ($desc['MissingWidth'] > 0)) { 05089 $dw = $desc['MissingWidth']; 05090 } elseif (isset($cw[32])) { 05091 $dw = $cw[32]; 05092 } else { 05093 $dw = 600; 05094 } 05095 } 05096 ++$this->numfonts; 05097 if ($type == 'core') { 05098 $name = $this->CoreFonts[$fontkey]; 05099 $subset = false; 05100 } elseif (($type == 'TrueType') OR ($type == 'Type1')) { 05101 $subset = false; 05102 } elseif ($type == 'TrueTypeUnicode') { 05103 $enc = 'Identity-H'; 05104 } elseif ($type == 'cidfont0') { 05105 if ($this->pdfa_mode) { 05106 $this->Error('All fonts must be embedded in PDF/A mode!'); 05107 } 05108 } else { 05109 $this->Error('Unknow font type: '.$type.''); 05110 } 05111 // set name if unset 05112 if (!isset($name) OR empty($name)) { 05113 $name = $fontkey; 05114 } 05115 // create artificial font style variations if missing (only works with non-embedded fonts) 05116 if (($type != 'core') AND $missing_style) { 05117 // style variations 05118 $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic'); 05119 $name .= $styles[$bistyle]; 05120 // artificial bold 05121 if (strpos($bistyle, 'B') !== false) { 05122 if (isset($desc['StemV'])) { 05123 // from normal to bold 05124 $desc['StemV'] = round($desc['StemV'] * 1.75); 05125 } else { 05126 // bold 05127 $desc['StemV'] = 123; 05128 } 05129 } 05130 // artificial italic 05131 if (strpos($bistyle, 'I') !== false) { 05132 if (isset($desc['ItalicAngle'])) { 05133 $desc['ItalicAngle'] -= 11; 05134 } else { 05135 $desc['ItalicAngle'] = -11; 05136 } 05137 if (isset($desc['Flags'])) { 05138 $desc['Flags'] |= 64; //bit 7 05139 } else { 05140 $desc['Flags'] = 64; 05141 } 05142 } 05143 } 05144 // initialize subsetchars to contain default ASCII values (0-255) 05145 $subsetchars = array_fill(0, 256, true); 05146 $this->setFontBuffer($fontkey, array('fontkey' => $fontkey, 'i' => $this->numfonts, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'dw' => $dw, 'enc' => $enc, 'cidinfo' => $cidinfo, 'file' => $file, 'ctg' => $ctg, 'subset' => $subset, 'subsetchars' => $subsetchars)); 05147 if ($this->inxobj) { 05148 // we are inside an XObject template 05149 $this->xobjects[$this->xobjid]['fonts'][$fontkey] = $this->numfonts; 05150 } 05151 if (isset($diff) AND (!empty($diff))) { 05152 //Search existing encodings 05153 $d = 0; 05154 $nb = count($this->diffs); 05155 for ($i=1; $i <= $nb; ++$i) { 05156 if ($this->diffs[$i] == $diff) { 05157 $d = $i; 05158 break; 05159 } 05160 } 05161 if ($d == 0) { 05162 $d = $nb + 1; 05163 $this->diffs[$d] = $diff; 05164 } 05165 $this->setFontSubBuffer($fontkey, 'diff', $d); 05166 } 05167 if (!$this->empty_string($file)) { 05168 if (!isset($this->FontFiles[$file])) { 05169 if ((strcasecmp($type,'TrueType') == 0) OR (strcasecmp($type, 'TrueTypeUnicode') == 0)) { 05170 $this->FontFiles[$file] = array('length1' => $originalsize, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey)); 05171 } elseif ($type != 'core') { 05172 $this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey)); 05173 } 05174 } else { 05175 // update fontkeys that are sharing this font file 05176 $this->FontFiles[$file]['subset'] = ($this->FontFiles[$file]['subset'] AND $subset); 05177 if (!in_array($fontkey, $this->FontFiles[$file]['fontkeys'])) { 05178 $this->FontFiles[$file]['fontkeys'][] = $fontkey; 05179 } 05180 } 05181 } 05182 return $fontdata; 05183 } 05184 05202 public function SetFont($family, $style='', $size=0, $fontfile='', $subset='default', $out=true) { 05203 //Select a font; size given in points 05204 if ($size == 0) { 05205 $size = $this->FontSizePt; 05206 } 05207 // try to add font (if not already added) 05208 $fontdata = $this->AddFont($family, $style, $fontfile, $subset); 05209 $this->FontFamily = $fontdata['family']; 05210 $this->FontStyle = $fontdata['style']; 05211 $this->CurrentFont = $this->getFontBuffer($fontdata['fontkey']); 05212 $this->SetFontSize($size, $out); 05213 } 05214 05223 public function SetFontSize($size, $out=true) { 05224 // font size in points 05225 $this->FontSizePt = $size; 05226 // font size in user units 05227 $this->FontSize = $size / $this->k; 05228 // calculate some font metrics 05229 if (isset($this->CurrentFont['desc']['FontBBox'])) { 05230 $bbox = explode(' ', substr($this->CurrentFont['desc']['FontBBox'], 1, -1)); 05231 $font_height = ((intval($bbox[3]) - intval($bbox[1])) * $size / 1000); 05232 } else { 05233 $font_height = $size * 1.219; 05234 } 05235 if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) { 05236 $font_ascent = ($this->CurrentFont['desc']['Ascent'] * $size / 1000); 05237 } 05238 if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] <= 0)) { 05239 $font_descent = (- $this->CurrentFont['desc']['Descent'] * $size / 1000); 05240 } 05241 if (!isset($font_ascent) AND !isset($font_descent)) { 05242 // core font 05243 $font_ascent = 0.76 * $font_height; 05244 $font_descent = $font_height - $font_ascent; 05245 } elseif (!isset($font_descent)) { 05246 $font_descent = $font_height - $font_ascent; 05247 } elseif (!isset($font_ascent)) { 05248 $font_ascent = $font_height - $font_descent; 05249 } 05250 $this->FontAscent = ($font_ascent / $this->k); 05251 $this->FontDescent = ($font_descent / $this->k); 05252 if ($out AND ($this->page > 0) AND (isset($this->CurrentFont['i']))) { 05253 $this->_out(sprintf('BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt)); 05254 } 05255 } 05256 05267 public function getFontDescent($font, $style='', $size=0) { 05268 $fontdata = $this->AddFont($font, $style); 05269 $fontinfo = $this->getFontBuffer($fontdata['fontkey']); 05270 if (isset($fontinfo['desc']['Descent']) AND ($fontinfo['desc']['Descent'] <= 0)) { 05271 $descent = (- $fontinfo['desc']['Descent'] * $size / 1000); 05272 } else { 05273 $descent = 1.219 * 0.24 * $size; 05274 } 05275 return ($descent / $this->k); 05276 } 05277 05288 public function getFontAscent($font, $style='', $size=0) { 05289 $fontdata = $this->AddFont($font, $style); 05290 $fontinfo = $this->getFontBuffer($fontdata['fontkey']); 05291 if (isset($fontinfo['desc']['Ascent']) AND ($fontinfo['desc']['Ascent'] > 0)) { 05292 $ascent = ($fontinfo['desc']['Ascent'] * $size / 1000); 05293 } else { 05294 $ascent = 1.219 * 0.76 * $size; 05295 } 05296 return ($ascent / $this->k); 05297 } 05298 05305 public function SetDefaultMonospacedFont($font) { 05306 $this->default_monospaced_font = $font; 05307 } 05308 05316 public function AddLink() { 05317 //Create a new internal link 05318 $n = count($this->links) + 1; 05319 $this->links[$n] = array(0, 0); 05320 return $n; 05321 } 05322 05332 public function SetLink($link, $y=0, $page=-1) { 05333 if ($y == -1) { 05334 $y = $this->y; 05335 } 05336 if ($page == -1) { 05337 $page = $this->page; 05338 } 05339 $this->links[$link] = array($page, $y); 05340 } 05341 05355 public function Link($x, $y, $w, $h, $link, $spaces=0) { 05356 $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'), $spaces); 05357 } 05358 05372 public function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text'), $spaces=0) { 05373 if ($this->inxobj) { 05374 // store parameters for later use on template 05375 $this->xobjects[$this->xobjid]['annotations'][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'text' => $text, 'opt' => $opt, 'spaces' => $spaces); 05376 return; 05377 } 05378 if ($x === '') { 05379 $x = $this->x; 05380 } 05381 if ($y === '') { 05382 $y = $this->y; 05383 } 05384 // check page for no-write regions and adapt page margins if necessary 05385 list($x, $y) = $this->checkPageRegions($h, $x, $y); 05386 // recalculate coordinates to account for graphic transformations 05387 if (isset($this->transfmatrix) AND !empty($this->transfmatrix)) { 05388 for ($i=$this->transfmatrix_key; $i > 0; --$i) { 05389 $maxid = count($this->transfmatrix[$i]) - 1; 05390 for ($j=$maxid; $j >= 0; --$j) { 05391 $ctm = $this->transfmatrix[$i][$j]; 05392 if (isset($ctm['a'])) { 05393 $x = $x * $this->k; 05394 $y = ($this->h - $y) * $this->k; 05395 $w = $w * $this->k; 05396 $h = $h * $this->k; 05397 // top left 05398 $xt = $x; 05399 $yt = $y; 05400 $x1 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e']; 05401 $y1 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f']; 05402 // top right 05403 $xt = $x + $w; 05404 $yt = $y; 05405 $x2 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e']; 05406 $y2 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f']; 05407 // bottom left 05408 $xt = $x; 05409 $yt = $y - $h; 05410 $x3 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e']; 05411 $y3 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f']; 05412 // bottom right 05413 $xt = $x + $w; 05414 $yt = $y - $h; 05415 $x4 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e']; 05416 $y4 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f']; 05417 // new coordinates (rectangle area) 05418 $x = min($x1, $x2, $x3, $x4); 05419 $y = max($y1, $y2, $y3, $y4); 05420 $w = (max($x1, $x2, $x3, $x4) - $x) / $this->k; 05421 $h = ($y - min($y1, $y2, $y3, $y4)) / $this->k; 05422 $x = $x / $this->k; 05423 $y = $this->h - ($y / $this->k); 05424 } 05425 } 05426 } 05427 } 05428 if ($this->page <= 0) { 05429 $page = 1; 05430 } else { 05431 $page = $this->page; 05432 } 05433 if (!isset($this->PageAnnots[$page])) { 05434 $this->PageAnnots[$page] = array(); 05435 } 05436 ++$this->n; 05437 $this->PageAnnots[$page][] = array('n' => $this->n, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces); 05438 if (!$this->pdfa_mode) { 05439 if ((($opt['Subtype'] == 'FileAttachment') OR ($opt['Subtype'] == 'Sound')) AND (!$this->empty_string($opt['FS'])) AND file_exists($opt['FS']) AND (!isset($this->embeddedfiles[basename($opt['FS'])]))) { 05440 ++$this->n; 05441 $this->embeddedfiles[basename($opt['FS'])] = array('n' => $this->n, 'file' => $opt['FS']); 05442 } 05443 } 05444 // Add widgets annotation's icons 05445 if (isset($opt['mk']['i']) AND file_exists($opt['mk']['i'])) { 05446 $this->Image($opt['mk']['i'], '', '', 10, 10, '', '', '', false, 300, '', false, false, 0, false, true); 05447 } 05448 if (isset($opt['mk']['ri']) AND file_exists($opt['mk']['ri'])) { 05449 $this->Image($opt['mk']['ri'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true); 05450 } 05451 if (isset($opt['mk']['ix']) AND file_exists($opt['mk']['ix'])) { 05452 $this->Image($opt['mk']['ix'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true); 05453 } 05454 } 05455 05462 protected function _putEmbeddedFiles() { 05463 if ($this->pdfa_mode) { 05464 // embedded files are not allowed in PDF/A mode 05465 return; 05466 } 05467 reset($this->embeddedfiles); 05468 foreach ($this->embeddedfiles as $filename => $filedata) { 05469 $data = file_get_contents($filedata['file']); 05470 $filter = ''; 05471 if ($this->compress) { 05472 $data = gzcompress($data); 05473 $filter = ' /Filter /FlateDecode'; 05474 } 05475 $stream = $this->_getrawstream($data, $filedata['n']); 05476 $out = $this->_getobj($filedata['n'])."\n"; 05477 $out .= '<< /Type /EmbeddedFile'.$filter.' /Length '.strlen($stream).' >>'; 05478 $out .= ' stream'."\n".$stream."\n".'endstream'; 05479 $out .= "\n".'endobj'; 05480 $this->_out($out); 05481 } 05482 } 05483 05507 public function Text($x, $y, $txt, $fstroke=false, $fclip=false, $ffill=true, $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M', $rtloff=false) { 05508 $textrendermode = $this->textrendermode; 05509 $textstrokewidth = $this->textstrokewidth; 05510 $this->setTextRenderingMode($fstroke, $ffill, $fclip); 05511 $this->SetXY($x, $y, $rtloff); 05512 $this->Cell(0, 0, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height, $calign, $valign); 05513 // restore previous rendering mode 05514 $this->textrendermode = $textrendermode; 05515 $this->textstrokewidth = $textstrokewidth; 05516 } 05517 05527 public function AcceptPageBreak() { 05528 if ($this->num_columns > 1) { 05529 // multi column mode 05530 if ($this->current_column < ($this->num_columns - 1)) { 05531 // go to next column 05532 $this->selectColumn($this->current_column + 1); 05533 } else { 05534 // add a new page 05535 $this->AddPage(); 05536 // set first column 05537 $this->selectColumn(0); 05538 } 05539 // avoid page breaking from checkPageBreak() 05540 return false; 05541 } 05542 return $this->AutoPageBreak; 05543 } 05544 05554 protected function checkPageBreak($h=0, $y='', $addpage=true) { 05555 if ($this->empty_string($y)) { 05556 $y = $this->y; 05557 } 05558 $current_page = $this->page; 05559 if ((($y + $h) > $this->PageBreakTrigger) AND ($this->inPageBody()) AND ($this->AcceptPageBreak())) { 05560 if ($addpage) { 05561 //Automatic page break 05562 $x = $this->x; 05563 $this->AddPage($this->CurOrientation); 05564 $this->y = $this->tMargin; 05565 $oldpage = $this->page - 1; 05566 if ($this->rtl) { 05567 if ($this->pagedim[$this->page]['orm'] != $this->pagedim[$oldpage]['orm']) { 05568 $this->x = $x - ($this->pagedim[$this->page]['orm'] - $this->pagedim[$oldpage]['orm']); 05569 } else { 05570 $this->x = $x; 05571 } 05572 } else { 05573 if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) { 05574 $this->x = $x + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$oldpage]['olm']); 05575 } else { 05576 $this->x = $x; 05577 } 05578 } 05579 } 05580 return true; 05581 } 05582 if ($current_page != $this->page) { 05583 // account for columns mode 05584 return true; 05585 } 05586 return false; 05587 } 05588 05605 public function removeSHY($txt='') { 05606 $txt = preg_replace('/([\\xc2]{1}[\\xad]{1})/', '', $txt); 05607 if (!$this->isunicode) { 05608 $txt = preg_replace('/([\\xad]{1})/', '', $txt); 05609 } 05610 return $txt; 05611 } 05612 05632 public function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M') { 05633 $prev_cell_margin = $this->cell_margin; 05634 $prev_cell_padding = $this->cell_padding; 05635 $this->adjustCellPadding($border); 05636 if (!$ignore_min_height) { 05637 $min_cell_height = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B']; 05638 if ($h < $min_cell_height) { 05639 $h = $min_cell_height; 05640 } 05641 } 05642 $this->checkPageBreak($h + $this->cell_margin['T'] + $this->cell_margin['B']); 05643 $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign)); 05644 $this->cell_padding = $prev_cell_padding; 05645 $this->cell_margin = $prev_cell_margin; 05646 } 05647 05668 protected function getCellCode($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M') { 05669 // replace 'NO-BREAK SPACE' (U+00A0) character with a simple space 05670 $txt = str_replace($this->unichr(160), ' ', $txt); 05671 $prev_cell_margin = $this->cell_margin; 05672 $prev_cell_padding = $this->cell_padding; 05673 $txt = $this->removeSHY($txt); 05674 $rs = ''; //string to be returned 05675 $this->adjustCellPadding($border); 05676 if (!$ignore_min_height) { 05677 $min_cell_height = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B']; 05678 if ($h < $min_cell_height) { 05679 $h = $min_cell_height; 05680 } 05681 } 05682 $k = $this->k; 05683 // check page for no-write regions and adapt page margins if necessary 05684 list($this->x, $this->y) = $this->checkPageRegions($h, $this->x, $this->y); 05685 if ($this->rtl) { 05686 $x = $this->x - $this->cell_margin['R']; 05687 } else { 05688 $x = $this->x + $this->cell_margin['L']; 05689 } 05690 $y = $this->y + $this->cell_margin['T']; 05691 $prev_font_stretching = $this->font_stretching; 05692 $prev_font_spacing = $this->font_spacing; 05693 // cell vertical alignment 05694 switch ($calign) { 05695 case 'A': { 05696 // font top 05697 switch ($valign) { 05698 case 'T': { 05699 // top 05700 $y -= $this->cell_padding['T']; 05701 break; 05702 } 05703 case 'B': { 05704 // bottom 05705 $y -= ($h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent); 05706 break; 05707 } 05708 default: 05709 case 'C': 05710 case 'M': { 05711 // center 05712 $y -= (($h - $this->FontAscent - $this->FontDescent) / 2); 05713 break; 05714 } 05715 } 05716 break; 05717 } 05718 case 'L': { 05719 // font baseline 05720 switch ($valign) { 05721 case 'T': { 05722 // top 05723 $y -= ($this->cell_padding['T'] + $this->FontAscent); 05724 break; 05725 } 05726 case 'B': { 05727 // bottom 05728 $y -= ($h - $this->cell_padding['B'] - $this->FontDescent); 05729 break; 05730 } 05731 default: 05732 case 'C': 05733 case 'M': { 05734 // center 05735 $y -= (($h + $this->FontAscent - $this->FontDescent) / 2); 05736 break; 05737 } 05738 } 05739 break; 05740 } 05741 case 'D': { 05742 // font bottom 05743 switch ($valign) { 05744 case 'T': { 05745 // top 05746 $y -= ($this->cell_padding['T'] + $this->FontAscent + $this->FontDescent); 05747 break; 05748 } 05749 case 'B': { 05750 // bottom 05751 $y -= ($h - $this->cell_padding['B']); 05752 break; 05753 } 05754 default: 05755 case 'C': 05756 case 'M': { 05757 // center 05758 $y -= (($h + $this->FontAscent + $this->FontDescent) / 2); 05759 break; 05760 } 05761 } 05762 break; 05763 } 05764 case 'B': { 05765 // cell bottom 05766 $y -= $h; 05767 break; 05768 } 05769 case 'C': 05770 case 'M': { 05771 // cell center 05772 $y -= ($h / 2); 05773 break; 05774 } 05775 default: 05776 case 'T': { 05777 // cell top 05778 break; 05779 } 05780 } 05781 // text vertical alignment 05782 switch ($valign) { 05783 case 'T': { 05784 // top 05785 $yt = $y + $this->cell_padding['T']; 05786 break; 05787 } 05788 case 'B': { 05789 // bottom 05790 $yt = $y + $h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent; 05791 break; 05792 } 05793 default: 05794 case 'C': 05795 case 'M': { 05796 // center 05797 $yt = $y + (($h - $this->FontAscent - $this->FontDescent) / 2); 05798 break; 05799 } 05800 } 05801 $basefonty = $yt + $this->FontAscent; 05802 if ($this->empty_string($w) OR ($w <= 0)) { 05803 if ($this->rtl) { 05804 $w = $x - $this->lMargin; 05805 } else { 05806 $w = $this->w - $this->rMargin - $x; 05807 } 05808 } 05809 $s = ''; 05810 // fill and borders 05811 if (is_string($border) AND (strlen($border) == 4)) { 05812 // full border 05813 $border = 1; 05814 } 05815 if ($fill OR ($border == 1)) { 05816 if ($fill) { 05817 $op = ($border == 1) ? 'B' : 'f'; 05818 } else { 05819 $op = 'S'; 05820 } 05821 if ($this->rtl) { 05822 $xk = (($x - $w) * $k); 05823 } else { 05824 $xk = ($x * $k); 05825 } 05826 $s .= sprintf('%.2F %.2F %.2F %.2F re %s ', $xk, (($this->h - $y) * $k), ($w * $k), (-$h * $k), $op); 05827 } 05828 // draw borders 05829 $s .= $this->getCellBorder($x, $y, $w, $h, $border); 05830 if ($txt != '') { 05831 $txt2 = $txt; 05832 if ($this->isunicode) { 05833 if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) { 05834 $txt2 = $this->UTF8ToLatin1($txt2); 05835 } else { 05836 $unicode = $this->UTF8StringToArray($txt); // array of UTF-8 unicode values 05837 $unicode = $this->utf8Bidi($unicode, '', $this->tmprtl); 05838 if (defined('K_THAI_TOPCHARS') AND (K_THAI_TOPCHARS == true)) { 05839 // ---- Fix for bug #2977340 "Incorrect Thai characters position arrangement" ---- 05840 // NOTE: this doesn't work with HTML justification 05841 // Symbols that could overlap on the font top (only works in LTR) 05842 $topchar = array(3611, 3613, 3615, 3650, 3651, 3652); // chars that extends on top 05843 $topsym = array(3633, 3636, 3637, 3638, 3639, 3655, 3656, 3657, 3658, 3659, 3660, 3661, 3662); // symbols with top position 05844 $numchars = count($unicode); // number of chars 05845 $unik = 0; 05846 $uniblock = array(); 05847 $uniblock[$unik] = array(); 05848 $uniblock[$unik][] = $unicode[0]; 05849 // resolve overlapping conflicts by splitting the string in several parts 05850 for ($i = 1; $i < $numchars; ++$i) { 05851 // check if symbols overlaps at top 05852 if (in_array($unicode[$i], $topsym) AND (in_array($unicode[($i - 1)], $topsym) OR in_array($unicode[($i - 1)], $topchar))) { 05853 // move symbols to another array 05854 ++$unik; 05855 $uniblock[$unik] = array(); 05856 $uniblock[$unik][] = $unicode[$i]; 05857 ++$unik; 05858 $uniblock[$unik] = array(); 05859 $unicode[$i] = 0x200b; // Unicode Character 'ZERO WIDTH SPACE' (DEC:8203, U+200B) 05860 } else { 05861 $uniblock[$unik][] = $unicode[$i]; 05862 } 05863 } 05864 // ---- END OF Fix for bug #2977340 05865 } 05866 $txt2 = $this->arrUTF8ToUTF16BE($unicode, false); 05867 } 05868 } 05869 $txt2 = $this->_escape($txt2); 05870 // get current text width (considering general font stretching and spacing) 05871 $txwidth = $this->GetStringWidth($txt); 05872 $width = $txwidth; 05873 // check for stretch mode 05874 if ($stretch > 0) { 05875 // calculate ratio between cell width and text width 05876 if ($width <= 0) { 05877 $ratio = 1; 05878 } else { 05879 $ratio = (($w - $this->cell_padding['L'] - $this->cell_padding['R']) / $width); 05880 } 05881 // check if stretching is required 05882 if (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0))) { 05883 // the text will be stretched to fit cell width 05884 if ($stretch > 2) { 05885 // set new character spacing 05886 $this->font_spacing += ($w - $this->cell_padding['L'] - $this->cell_padding['R'] - $width) / (max(($this->GetNumChars($txt) - 1), 1) * ($this->font_stretching / 100)); 05887 } else { 05888 // set new horizontal stretching 05889 $this->font_stretching *= $ratio; 05890 } 05891 // recalculate text width (the text fills the entire cell) 05892 $width = $w - $this->cell_padding['L'] - $this->cell_padding['R']; 05893 // reset alignment 05894 $align = ''; 05895 } 05896 } 05897 if ($this->font_stretching != 100) { 05898 // apply font stretching 05899 $rs .= sprintf('BT %.2F Tz ET ', $this->font_stretching); 05900 } 05901 if ($this->font_spacing != 0) { 05902 // increase/decrease font spacing 05903 $rs .= sprintf('BT %.2F Tc ET ', ($this->font_spacing * $this->k)); 05904 } 05905 if ($this->ColorFlag AND ($this->textrendermode < 4)) { 05906 $s .= 'q '.$this->TextColor.' '; 05907 } 05908 // rendering mode 05909 $s .= sprintf('BT %d Tr %.2F w ET ', $this->textrendermode, $this->textstrokewidth); 05910 // count number of spaces 05911 $ns = substr_count($txt, chr(32)); 05912 // Justification 05913 $spacewidth = 0; 05914 if (($align == 'J') AND ($ns > 0)) { 05915 if ($this->isUnicodeFont()) { 05916 // get string width without spaces 05917 $width = $this->GetStringWidth(str_replace(' ', '', $txt)); 05918 // calculate average space width 05919 $spacewidth = -1000 * ($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1) / $this->FontSize; 05920 if ($this->font_stretching != 100) { 05921 // word spacing is affected by stretching 05922 $spacewidth /= ($this->font_stretching / 100); 05923 } 05924 // set word position to be used with TJ operator 05925 $txt2 = str_replace(chr(0).chr(32), ') '.sprintf('%.3F', $spacewidth).' (', $txt2); 05926 $unicode_justification = true; 05927 } else { 05928 // get string width 05929 $width = $txwidth; 05930 // new space width 05931 $spacewidth = (($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1)) * $this->k; 05932 if ($this->font_stretching != 100) { 05933 // word spacing (Tw) is affected by stretching 05934 $spacewidth /= ($this->font_stretching / 100); 05935 } 05936 // set word spacing 05937 $rs .= sprintf('BT %.3F Tw ET ', $spacewidth); 05938 } 05939 $width = $w - $this->cell_padding['L'] - $this->cell_padding['R']; 05940 } 05941 // replace carriage return characters 05942 $txt2 = str_replace("\r", ' ', $txt2); 05943 switch ($align) { 05944 case 'C': { 05945 $dx = ($w - $width) / 2; 05946 break; 05947 } 05948 case 'R': { 05949 if ($this->rtl) { 05950 $dx = $this->cell_padding['R']; 05951 } else { 05952 $dx = $w - $width - $this->cell_padding['R']; 05953 } 05954 break; 05955 } 05956 case 'L': { 05957 if ($this->rtl) { 05958 $dx = $w - $width - $this->cell_padding['L']; 05959 } else { 05960 $dx = $this->cell_padding['L']; 05961 } 05962 break; 05963 } 05964 case 'J': 05965 default: { 05966 if ($this->rtl) { 05967 $dx = $this->cell_padding['R']; 05968 } else { 05969 $dx = $this->cell_padding['L']; 05970 } 05971 break; 05972 } 05973 } 05974 if ($this->rtl) { 05975 $xdx = $x - $dx - $width; 05976 } else { 05977 $xdx = $x + $dx; 05978 } 05979 $xdk = $xdx * $k; 05980 // print text 05981 $s .= sprintf('BT %.2F %.2F Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2); 05982 if (isset($uniblock)) { 05983 // print overlapping characters as separate string 05984 $xshift = 0; // horizontal shift 05985 $ty = (($this->h - $basefonty + (0.2 * $this->FontSize)) * $k); 05986 $spw = (($w - $txwidth - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1)); 05987 foreach ($uniblock as $uk => $uniarr) { 05988 if (($uk % 2) == 0) { 05989 // x space to skip 05990 if ($spacewidth != 0) { 05991 // justification shift 05992 $xshift += (count(array_keys($uniarr, 32)) * $spw); 05993 } 05994 $xshift += $this->GetArrStringWidth($uniarr); // + shift justification 05995 } else { 05996 // character to print 05997 $topchr = $this->arrUTF8ToUTF16BE($uniarr, false); 05998 $topchr = $this->_escape($topchr); 05999 $s .= sprintf(' BT %.2F %.2F Td [(%s)] TJ ET', ($xdk + ($xshift * $k)), $ty, $topchr); 06000 } 06001 } 06002 } 06003 if ($this->underline) { 06004 $s .= ' '.$this->_dounderlinew($xdx, $basefonty, $width); 06005 } 06006 if ($this->linethrough) { 06007 $s .= ' '.$this->_dolinethroughw($xdx, $basefonty, $width); 06008 } 06009 if ($this->overline) { 06010 $s .= ' '.$this->_dooverlinew($xdx, $basefonty, $width); 06011 } 06012 if ($this->ColorFlag AND ($this->textrendermode < 4)) { 06013 $s .= ' Q'; 06014 } 06015 if ($link) { 06016 $this->Link($xdx, $yt, $width, ($this->FontAscent + $this->FontDescent), $link, $ns); 06017 } 06018 } 06019 // output cell 06020 if ($s) { 06021 // output cell 06022 $rs .= $s; 06023 if ($this->font_spacing != 0) { 06024 // reset font spacing mode 06025 $rs .= ' BT 0 Tc ET'; 06026 } 06027 if ($this->font_stretching != 100) { 06028 // reset font stretching mode 06029 $rs .= ' BT 100 Tz ET'; 06030 } 06031 } 06032 // reset word spacing 06033 if (!$this->isUnicodeFont() AND ($align == 'J')) { 06034 $rs .= ' BT 0 Tw ET'; 06035 } 06036 // reset stretching and spacing 06037 $this->font_stretching = $prev_font_stretching; 06038 $this->font_spacing = $prev_font_spacing; 06039 $this->lasth = $h; 06040 if ($ln > 0) { 06041 //Go to the beginning of the next line 06042 $this->y = $y + $h + $this->cell_margin['B']; 06043 if ($ln == 1) { 06044 if ($this->rtl) { 06045 $this->x = $this->w - $this->rMargin; 06046 } else { 06047 $this->x = $this->lMargin; 06048 } 06049 } 06050 } else { 06051 // go left or right by case 06052 if ($this->rtl) { 06053 $this->x = $x - $w - $this->cell_margin['L']; 06054 } else { 06055 $this->x = $x + $w + $this->cell_margin['R']; 06056 } 06057 } 06058 $gstyles = ''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor."\n"; 06059 $rs = $gstyles.$rs; 06060 $this->cell_padding = $prev_cell_padding; 06061 $this->cell_margin = $prev_cell_margin; 06062 return $rs; 06063 } 06064 06077 protected function getCellBorder($x, $y, $w, $h, $brd) { 06078 $s = ''; // string to be returned 06079 if (empty($brd)) { 06080 return $s; 06081 } 06082 if ($brd == 1) { 06083 $brd = array('LRTB' => true); 06084 } 06085 // calculate coordinates for border 06086 $k = $this->k; 06087 if ($this->rtl) { 06088 $xeL = ($x - $w) * $k; 06089 $xeR = $x * $k; 06090 } else { 06091 $xeL = $x * $k; 06092 $xeR = ($x + $w) * $k; 06093 } 06094 $yeL = (($this->h - ($y + $h)) * $k); 06095 $yeT = (($this->h - $y) * $k); 06096 $xeT = $xeL; 06097 $xeB = $xeR; 06098 $yeR = $yeT; 06099 $yeB = $yeL; 06100 if (is_string($brd)) { 06101 // convert string to array 06102 $slen = strlen($brd); 06103 $newbrd = array(); 06104 for ($i = 0; $i < $slen; ++$i) { 06105 $newbrd[$brd{$i}] = array('cap' => 'square', 'join' => 'miter'); 06106 } 06107 $brd = $newbrd; 06108 } 06109 if (isset($brd['mode'])) { 06110 $mode = $brd['mode']; 06111 unset($brd['mode']); 06112 } else { 06113 $mode = 'normal'; 06114 } 06115 foreach ($brd as $border => $style) { 06116 if (is_array($style) AND !empty($style)) { 06117 // apply border style 06118 $prev_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '; 06119 $s .= $this->SetLineStyle($style, true)."\n"; 06120 } 06121 switch ($mode) { 06122 case 'ext': { 06123 $off = (($this->LineWidth / 2) * $k); 06124 $xL = $xeL - $off; 06125 $xR = $xeR + $off; 06126 $yT = $yeT + $off; 06127 $yL = $yeL - $off; 06128 $xT = $xL; 06129 $xB = $xR; 06130 $yR = $yT; 06131 $yB = $yL; 06132 $w += $this->LineWidth; 06133 $h += $this->LineWidth; 06134 break; 06135 } 06136 case 'int': { 06137 $off = ($this->LineWidth / 2) * $k; 06138 $xL = $xeL + $off; 06139 $xR = $xeR - $off; 06140 $yT = $yeT - $off; 06141 $yL = $yeL + $off; 06142 $xT = $xL; 06143 $xB = $xR; 06144 $yR = $yT; 06145 $yB = $yL; 06146 $w -= $this->LineWidth; 06147 $h -= $this->LineWidth; 06148 break; 06149 } 06150 case 'normal': 06151 default: { 06152 $xL = $xeL; 06153 $xT = $xeT; 06154 $xB = $xeB; 06155 $xR = $xeR; 06156 $yL = $yeL; 06157 $yT = $yeT; 06158 $yB = $yeB; 06159 $yR = $yeR; 06160 break; 06161 } 06162 } 06163 // draw borders by case 06164 if (strlen($border) == 4) { 06165 $s .= sprintf('%.2F %.2F %.2F %.2F re S ', $xT, $yT, ($w * $k), (-$h * $k)); 06166 } elseif (strlen($border) == 3) { 06167 if (strpos($border,'B') === false) { // LTR 06168 $s .= sprintf('%.2F %.2F m ', $xL, $yL); 06169 $s .= sprintf('%.2F %.2F l ', $xT, $yT); 06170 $s .= sprintf('%.2F %.2F l ', $xR, $yR); 06171 $s .= sprintf('%.2F %.2F l ', $xB, $yB); 06172 $s .= 'S '; 06173 } elseif (strpos($border,'L') === false) { // TRB 06174 $s .= sprintf('%.2F %.2F m ', $xT, $yT); 06175 $s .= sprintf('%.2F %.2F l ', $xR, $yR); 06176 $s .= sprintf('%.2F %.2F l ', $xB, $yB); 06177 $s .= sprintf('%.2F %.2F l ', $xL, $yL); 06178 $s .= 'S '; 06179 } elseif (strpos($border,'T') === false) { // RBL 06180 $s .= sprintf('%.2F %.2F m ', $xR, $yR); 06181 $s .= sprintf('%.2F %.2F l ', $xB, $yB); 06182 $s .= sprintf('%.2F %.2F l ', $xL, $yL); 06183 $s .= sprintf('%.2F %.2F l ', $xT, $yT); 06184 $s .= 'S '; 06185 } elseif (strpos($border,'R') === false) { // BLT 06186 $s .= sprintf('%.2F %.2F m ', $xB, $yB); 06187 $s .= sprintf('%.2F %.2F l ', $xL, $yL); 06188 $s .= sprintf('%.2F %.2F l ', $xT, $yT); 06189 $s .= sprintf('%.2F %.2F l ', $xR, $yR); 06190 $s .= 'S '; 06191 } 06192 } elseif (strlen($border) == 2) { 06193 if ((strpos($border,'L') !== false) AND (strpos($border,'T') !== false)) { // LT 06194 $s .= sprintf('%.2F %.2F m ', $xL, $yL); 06195 $s .= sprintf('%.2F %.2F l ', $xT, $yT); 06196 $s .= sprintf('%.2F %.2F l ', $xR, $yR); 06197 $s .= 'S '; 06198 } elseif ((strpos($border,'T') !== false) AND (strpos($border,'R') !== false)) { // TR 06199 $s .= sprintf('%.2F %.2F m ', $xT, $yT); 06200 $s .= sprintf('%.2F %.2F l ', $xR, $yR); 06201 $s .= sprintf('%.2F %.2F l ', $xB, $yB); 06202 $s .= 'S '; 06203 } elseif ((strpos($border,'R') !== false) AND (strpos($border,'B') !== false)) { // RB 06204 $s .= sprintf('%.2F %.2F m ', $xR, $yR); 06205 $s .= sprintf('%.2F %.2F l ', $xB, $yB); 06206 $s .= sprintf('%.2F %.2F l ', $xL, $yL); 06207 $s .= 'S '; 06208 } elseif ((strpos($border,'B') !== false) AND (strpos($border,'L') !== false)) { // BL 06209 $s .= sprintf('%.2F %.2F m ', $xB, $yB); 06210 $s .= sprintf('%.2F %.2F l ', $xL, $yL); 06211 $s .= sprintf('%.2F %.2F l ', $xT, $yT); 06212 $s .= 'S '; 06213 } elseif ((strpos($border,'L') !== false) AND (strpos($border,'R') !== false)) { // LR 06214 $s .= sprintf('%.2F %.2F m ', $xL, $yL); 06215 $s .= sprintf('%.2F %.2F l ', $xT, $yT); 06216 $s .= 'S '; 06217 $s .= sprintf('%.2F %.2F m ', $xR, $yR); 06218 $s .= sprintf('%.2F %.2F l ', $xB, $yB); 06219 $s .= 'S '; 06220 } elseif ((strpos($border,'T') !== false) AND (strpos($border,'B') !== false)) { // TB 06221 $s .= sprintf('%.2F %.2F m ', $xT, $yT); 06222 $s .= sprintf('%.2F %.2F l ', $xR, $yR); 06223 $s .= 'S '; 06224 $s .= sprintf('%.2F %.2F m ', $xB, $yB); 06225 $s .= sprintf('%.2F %.2F l ', $xL, $yL); 06226 $s .= 'S '; 06227 } 06228 } else { // strlen($border) == 1 06229 if (strpos($border,'L') !== false) { // L 06230 $s .= sprintf('%.2F %.2F m ', $xL, $yL); 06231 $s .= sprintf('%.2F %.2F l ', $xT, $yT); 06232 $s .= 'S '; 06233 } elseif (strpos($border,'T') !== false) { // T 06234 $s .= sprintf('%.2F %.2F m ', $xT, $yT); 06235 $s .= sprintf('%.2F %.2F l ', $xR, $yR); 06236 $s .= 'S '; 06237 } elseif (strpos($border,'R') !== false) { // R 06238 $s .= sprintf('%.2F %.2F m ', $xR, $yR); 06239 $s .= sprintf('%.2F %.2F l ', $xB, $yB); 06240 $s .= 'S '; 06241 } elseif (strpos($border,'B') !== false) { // B 06242 $s .= sprintf('%.2F %.2F m ', $xB, $yB); 06243 $s .= sprintf('%.2F %.2F l ', $xL, $yL); 06244 $s .= 'S '; 06245 } 06246 } 06247 if (is_array($style) AND !empty($style)) { 06248 // reset border style to previous value 06249 $s .= "\n".$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor."\n"; 06250 } 06251 } 06252 return $s; 06253 } 06254 06280 public function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=false, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0, $valign='T', $fitcell=false) { 06281 $prev_cell_margin = $this->cell_margin; 06282 $prev_cell_padding = $this->cell_padding; 06283 // adjust internal padding 06284 $this->adjustCellPadding($border); 06285 $mc_padding = $this->cell_padding; 06286 $mc_margin = $this->cell_margin; 06287 $this->cell_padding['T'] = 0; 06288 $this->cell_padding['B'] = 0; 06289 $this->setCellMargins(0, 0, 0, 0); 06290 if ($this->empty_string($this->lasth) OR $reseth) { 06291 // reset row height 06292 $this->resetLastH(); 06293 } 06294 if (!$this->empty_string($y)) { 06295 $this->SetY($y); 06296 } else { 06297 $y = $this->GetY(); 06298 } 06299 $resth = 0; 06300 if (($h > 0) AND $this->inPageBody() AND (($y + $h + $mc_margin['T'] + $mc_margin['B']) > $this->PageBreakTrigger)) { 06301 // spit cell in more pages/columns 06302 $newh = ($this->PageBreakTrigger - $y); 06303 $resth = ($h - $newh); // cell to be printed on the next page/column 06304 $h = $newh; 06305 } 06306 // get current page number 06307 $startpage = $this->page; 06308 // get current column 06309 $startcolumn = $this->current_column; 06310 if (!$this->empty_string($x)) { 06311 $this->SetX($x); 06312 } else { 06313 $x = $this->GetX(); 06314 } 06315 // check page for no-write regions and adapt page margins if necessary 06316 list($x, $y) = $this->checkPageRegions(0, $x, $y); 06317 // apply margins 06318 $oy = $y + $mc_margin['T']; 06319 if ($this->rtl) { 06320 $ox = $this->w - $x - $mc_margin['R']; 06321 } else { 06322 $ox = $x + $mc_margin['L']; 06323 } 06324 $this->x = $ox; 06325 $this->y = $oy; 06326 // set width 06327 if ($this->empty_string($w) OR ($w <= 0)) { 06328 if ($this->rtl) { 06329 $w = $this->x - $this->lMargin - $mc_margin['L']; 06330 } else { 06331 $w = $this->w - $this->x - $this->rMargin - $mc_margin['R']; 06332 } 06333 } 06334 // store original margin values 06335 $lMargin = $this->lMargin; 06336 $rMargin = $this->rMargin; 06337 if ($this->rtl) { 06338 $this->rMargin = $this->w - $this->x; 06339 $this->lMargin = $this->x - $w; 06340 } else { 06341 $this->lMargin = $this->x; 06342 $this->rMargin = $this->w - $this->x - $w; 06343 } 06344 if ($autopadding) { 06345 // add top padding 06346 $this->y += $mc_padding['T']; 06347 } 06348 if ($ishtml) { // ******* Write HTML text 06349 $this->writeHTML($txt, true, false, $reseth, true, $align); 06350 $nl = 1; 06351 } else { // ******* Write simple text 06352 $prev_FontSizePt = $this->FontSizePt; 06353 // vertical alignment 06354 if ($maxh > 0) { 06355 // get text height 06356 $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border); 06357 if ($fitcell) { 06358 // try to reduce font size to fit text on cell (use a quick search algorithm) 06359 $fmin = 1; 06360 $fmax = $this->FontSizePt; 06361 $prev_text_height = $text_height; 06362 $maxit = 100; // max number of iterations 06363 while ($maxit > 0) { 06364 $fmid = (($fmax + $fmin) / 2); 06365 $this->SetFontSize($fmid, false); 06366 $this->resetLastH(); 06367 $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border); 06368 if (($text_height == $maxh) OR (($text_height < $maxh) AND ($fmin >= ($fmax - 0.01)))) { 06369 break; 06370 } elseif ($text_height < $maxh) { 06371 $fmin = $fmid; 06372 } else { 06373 $fmax = $fmid; 06374 } 06375 --$maxit; 06376 } 06377 $this->SetFontSize($this->FontSizePt); 06378 } 06379 if ($text_height < $maxh) { 06380 if ($valign == 'M') { 06381 // text vertically centered 06382 $this->y += (($maxh - $text_height) / 2); 06383 } elseif ($valign == 'B') { 06384 // text vertically aligned on bottom 06385 $this->y += ($maxh - $text_height); 06386 } 06387 } 06388 } 06389 $nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false, true, $maxh, 0, $mc_margin); 06390 if ($fitcell) { 06391 // restore font size 06392 $this->SetFontSize($prev_FontSizePt); 06393 } 06394 } 06395 if ($autopadding) { 06396 // add bottom padding 06397 $this->y += $mc_padding['B']; 06398 } 06399 // Get end-of-text Y position 06400 $currentY = $this->y; 06401 // get latest page number 06402 $endpage = $this->page; 06403 if ($resth > 0) { 06404 $skip = ($endpage - $startpage); 06405 $tmpresth = $resth; 06406 while ($tmpresth > 0) { 06407 if ($skip <= 0) { 06408 // add a page (or trig AcceptPageBreak() for multicolumn mode) 06409 $this->checkPageBreak($this->PageBreakTrigger + 1); 06410 } 06411 if ($this->num_columns > 1) { 06412 $tmpresth -= ($this->h - $this->y - $this->bMargin); 06413 } else { 06414 $tmpresth -= ($this->h - $this->tMargin - $this->bMargin); 06415 } 06416 --$skip; 06417 } 06418 $currentY = $this->y; 06419 $endpage = $this->page; 06420 } 06421 // get latest column 06422 $endcolumn = $this->current_column; 06423 if ($this->num_columns == 0) { 06424 $this->num_columns = 1; 06425 } 06426 // get border modes 06427 $border_start = $this->getBorderMode($border, $position='start'); 06428 $border_end = $this->getBorderMode($border, $position='end'); 06429 $border_middle = $this->getBorderMode($border, $position='middle'); 06430 // design borders around HTML cells. 06431 for ($page = $startpage; $page <= $endpage; ++$page) { // for each page 06432 $ccode = ''; 06433 $this->setPage($page); 06434 if ($this->num_columns < 2) { 06435 // single-column mode 06436 $this->SetX($x); 06437 $this->y = $this->tMargin; 06438 } 06439 // account for margin changes 06440 if ($page > $startpage) { 06441 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) { 06442 $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']); 06443 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) { 06444 $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']); 06445 } 06446 } 06447 if ($startpage == $endpage) { 06448 // single page 06449 for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column 06450 $this->selectColumn($column); 06451 if ($this->rtl) { 06452 $this->x -= $mc_margin['R']; 06453 } else { 06454 $this->x += $mc_margin['L']; 06455 } 06456 if ($startcolumn == $endcolumn) { // single column 06457 $cborder = $border; 06458 $h = max($h, ($currentY - $oy)); 06459 $this->y = $oy; 06460 } elseif ($column == $startcolumn) { // first column 06461 $cborder = $border_start; 06462 $this->y = $oy; 06463 $h = $this->h - $this->y - $this->bMargin; 06464 } elseif ($column == $endcolumn) { // end column 06465 $cborder = $border_end; 06466 $h = $currentY - $this->y; 06467 if ($resth > $h) { 06468 $h = $resth; 06469 } 06470 } else { // middle column 06471 $cborder = $border_middle; 06472 $h = $this->h - $this->y - $this->bMargin; 06473 $resth -= $h; 06474 } 06475 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 06476 } // end for each column 06477 } elseif ($page == $startpage) { // first page 06478 for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column 06479 $this->selectColumn($column); 06480 if ($this->rtl) { 06481 $this->x -= $mc_margin['R']; 06482 } else { 06483 $this->x += $mc_margin['L']; 06484 } 06485 if ($column == $startcolumn) { // first column 06486 $cborder = $border_start; 06487 $this->y = $oy; 06488 $h = $this->h - $this->y - $this->bMargin; 06489 } else { // middle column 06490 $cborder = $border_middle; 06491 $h = $this->h - $this->y - $this->bMargin; 06492 $resth -= $h; 06493 } 06494 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 06495 } // end for each column 06496 } elseif ($page == $endpage) { // last page 06497 for ($column = 0; $column <= $endcolumn; ++$column) { // for each column 06498 $this->selectColumn($column); 06499 if ($this->rtl) { 06500 $this->x -= $mc_margin['R']; 06501 } else { 06502 $this->x += $mc_margin['L']; 06503 } 06504 if ($column == $endcolumn) { 06505 // end column 06506 $cborder = $border_end; 06507 $h = $currentY - $this->y; 06508 if ($resth > $h) { 06509 $h = $resth; 06510 } 06511 } else { 06512 // middle column 06513 $cborder = $border_middle; 06514 $h = $this->h - $this->y - $this->bMargin; 06515 $resth -= $h; 06516 } 06517 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 06518 } // end for each column 06519 } else { // middle page 06520 for ($column = 0; $column < $this->num_columns; ++$column) { // for each column 06521 $this->selectColumn($column); 06522 if ($this->rtl) { 06523 $this->x -= $mc_margin['R']; 06524 } else { 06525 $this->x += $mc_margin['L']; 06526 } 06527 $cborder = $border_middle; 06528 $h = $this->h - $this->y - $this->bMargin; 06529 $resth -= $h; 06530 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 06531 } // end for each column 06532 } 06533 if ($cborder OR $fill) { 06534 $offsetlen = strlen($ccode); 06535 // draw border and fill 06536 if ($this->inxobj) { 06537 // we are inside an XObject template 06538 if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) { 06539 $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']); 06540 $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey]; 06541 $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen; 06542 } else { 06543 $pagemark = $this->xobjects[$this->xobjid]['intmrk']; 06544 $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen; 06545 } 06546 $pagebuff = $this->xobjects[$this->xobjid]['outdata']; 06547 $pstart = substr($pagebuff, 0, $pagemark); 06548 $pend = substr($pagebuff, $pagemark); 06549 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend; 06550 } else { 06551 if (end($this->transfmrk[$this->page]) !== false) { 06552 $pagemarkkey = key($this->transfmrk[$this->page]); 06553 $pagemark = $this->transfmrk[$this->page][$pagemarkkey]; 06554 $this->transfmrk[$this->page][$pagemarkkey] += $offsetlen; 06555 } elseif ($this->InFooter) { 06556 $pagemark = $this->footerpos[$this->page]; 06557 $this->footerpos[$this->page] += $offsetlen; 06558 } else { 06559 $pagemark = $this->intmrk[$this->page]; 06560 $this->intmrk[$this->page] += $offsetlen; 06561 } 06562 $pagebuff = $this->getPageBuffer($this->page); 06563 $pstart = substr($pagebuff, 0, $pagemark); 06564 $pend = substr($pagebuff, $pagemark); 06565 $this->setPageBuffer($this->page, $pstart.$ccode.$pend); 06566 } 06567 } 06568 } // end for each page 06569 // Get end-of-cell Y position 06570 $currentY = $this->GetY(); 06571 // restore previous values 06572 if ($this->num_columns > 1) { 06573 $this->selectColumn(); 06574 } else { 06575 // restore original margins 06576 $this->lMargin = $lMargin; 06577 $this->rMargin = $rMargin; 06578 if ($this->page > $startpage) { 06579 // check for margin variations between pages (i.e. booklet mode) 06580 $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$startpage]['olm']); 06581 $dr = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$startpage]['orm']); 06582 if (($dl != 0) OR ($dr != 0)) { 06583 $this->lMargin += $dl; 06584 $this->rMargin += $dr; 06585 } 06586 } 06587 } 06588 if ($ln > 0) { 06589 //Go to the beginning of the next line 06590 $this->SetY($currentY + $mc_margin['B']); 06591 if ($ln == 2) { 06592 $this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']); 06593 } 06594 } else { 06595 // go left or right by case 06596 $this->setPage($startpage); 06597 $this->y = $y; 06598 $this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']); 06599 } 06600 $this->setContentMark(); 06601 $this->cell_padding = $prev_cell_padding; 06602 $this->cell_margin = $prev_cell_margin; 06603 return $nl; 06604 } 06605 06614 protected function getBorderMode($brd, $position='start') { 06615 if ((!$this->opencell) OR empty($brd)) { 06616 return $brd; 06617 } 06618 if ($brd == 1) { 06619 $brd = 'LTRB'; 06620 } 06621 if (is_string($brd)) { 06622 // convert string to array 06623 $slen = strlen($brd); 06624 $newbrd = array(); 06625 for ($i = 0; $i < $slen; ++$i) { 06626 $newbrd[$brd{$i}] = array('cap' => 'square', 'join' => 'miter'); 06627 } 06628 $brd = $newbrd; 06629 } 06630 foreach ($brd as $border => $style) { 06631 switch ($position) { 06632 case 'start': { 06633 if (strpos($border, 'B') !== false) { 06634 // remove bottom line 06635 $newkey = str_replace('B', '', $border); 06636 if (strlen($newkey) > 0) { 06637 $brd[$newkey] = $style; 06638 } 06639 unset($brd[$border]); 06640 } 06641 break; 06642 } 06643 case 'middle': { 06644 if (strpos($border, 'B') !== false) { 06645 // remove bottom line 06646 $newkey = str_replace('B', '', $border); 06647 if (strlen($newkey) > 0) { 06648 $brd[$newkey] = $style; 06649 } 06650 unset($brd[$border]); 06651 $border = $newkey; 06652 } 06653 if (strpos($border, 'T') !== false) { 06654 // remove bottom line 06655 $newkey = str_replace('T', '', $border); 06656 if (strlen($newkey) > 0) { 06657 $brd[$newkey] = $style; 06658 } 06659 unset($brd[$border]); 06660 } 06661 break; 06662 } 06663 case 'end': { 06664 if (strpos($border, 'T') !== false) { 06665 // remove bottom line 06666 $newkey = str_replace('T', '', $border); 06667 if (strlen($newkey) > 0) { 06668 $brd[$newkey] = $style; 06669 } 06670 unset($brd[$border]); 06671 } 06672 break; 06673 } 06674 } 06675 } 06676 return $brd; 06677 } 06678 06692 public function getNumLines($txt, $w=0, $reseth=false, $autopadding=true, $cellpadding='', $border=0) { 06693 if ($txt === '') { 06694 // empty string 06695 return 1; 06696 } 06697 // adjust internal padding 06698 $prev_cell_padding = $this->cell_padding; 06699 $prev_lasth = $this->lasth; 06700 if (is_array($cellpadding)) { 06701 $this->cell_padding = $cellpadding; 06702 } 06703 $this->adjustCellPadding($border); 06704 if ($this->empty_string($w) OR ($w <= 0)) { 06705 if ($this->rtl) { 06706 $w = $this->x - $this->lMargin; 06707 } else { 06708 $w = $this->w - $this->rMargin - $this->x; 06709 } 06710 } 06711 $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R']; 06712 if ($reseth) { 06713 // reset row height 06714 $this->resetLastH(); 06715 } 06716 $lines = 1; 06717 $sum = 0; 06718 $chars = $this->utf8Bidi($this->UTF8StringToArray($txt), $txt, $this->tmprtl); 06719 $charsWidth = $this->GetArrStringWidth($chars, '', '', 0, true); 06720 $length = count($chars); 06721 $lastSeparator = -1; 06722 for ($i = 0; $i < $length; ++$i) { 06723 $charWidth = $charsWidth[$i]; 06724 if (preg_match($this->re_spaces, $this->unichr($chars[$i]))) { 06725 $lastSeparator = $i; 06726 } 06727 if ((($sum + $charWidth) > $wmax) OR ($chars[$i] == 10)) { 06728 ++$lines; 06729 if ($chars[$i] == 10) { 06730 $lastSeparator = -1; 06731 $sum = 0; 06732 } elseif ($lastSeparator != -1) { 06733 $i = $lastSeparator; 06734 $lastSeparator = -1; 06735 $sum = 0; 06736 } else { 06737 $sum = $charWidth; 06738 } 06739 } else { 06740 $sum += $charWidth; 06741 } 06742 } 06743 if ($chars[($length - 1)] == 10) { 06744 --$lines; 06745 } 06746 $this->cell_padding = $prev_cell_padding; 06747 $this->lasth = $prev_lasth; 06748 return $lines; 06749 } 06750 06798 public function getStringHeight($w, $txt, $reseth=false, $autopadding=true, $cellpadding='', $border=0) { 06799 // adjust internal padding 06800 $prev_cell_padding = $this->cell_padding; 06801 $prev_lasth = $this->lasth; 06802 if (is_array($cellpadding)) { 06803 $this->cell_padding = $cellpadding; 06804 } 06805 $this->adjustCellPadding($border); 06806 $lines = $this->getNumLines($txt, $w, $reseth, $autopadding, $cellpadding, $border); 06807 $height = $lines * ($this->FontSize * $this->cell_height_ratio); 06808 if ($autopadding) { 06809 // add top and bottom padding 06810 $height += ($this->cell_padding['T'] + $this->cell_padding['B']); 06811 } 06812 $this->cell_padding = $prev_cell_padding; 06813 $this->lasth = $prev_lasth; 06814 return $height; 06815 } 06816 06835 public function Write($h, $txt, $link='', $fill=false, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0, $wadj=0, $margin='') { 06836 // check page for no-write regions and adapt page margins if necessary 06837 list($this->x, $this->y) = $this->checkPageRegions($h, $this->x, $this->y); 06838 if (strlen($txt) == 0) { 06839 // fix empty text 06840 $txt = ' '; 06841 } 06842 if ($margin === '') { 06843 // set default margins 06844 $margin = $this->cell_margin; 06845 } 06846 // remove carriage returns 06847 $s = str_replace("\r", '', $txt); 06848 // check if string contains arabic text 06849 if (preg_match($this->unicode->uni_RE_PATTERN_ARABIC, $s)) { 06850 $arabic = true; 06851 } else { 06852 $arabic = false; 06853 } 06854 // check if string contains RTL text 06855 if ($arabic OR ($this->tmprtl == 'R') OR preg_match($this->unicode->uni_RE_PATTERN_RTL, $s)) { 06856 $rtlmode = true; 06857 } else { 06858 $rtlmode = false; 06859 } 06860 // get a char width 06861 $chrwidth = $this->GetCharWidth(46); // dot character 06862 // get array of unicode values 06863 $chars = $this->UTF8StringToArray($s); 06864 // get array of chars 06865 $uchars = $this->UTF8ArrayToUniArray($chars); 06866 // get the number of characters 06867 $nb = count($chars); 06868 // replacement for SHY character (minus symbol) 06869 $shy_replacement = 45; 06870 $shy_replacement_char = $this->unichr($shy_replacement); 06871 // widht for SHY replacement 06872 $shy_replacement_width = $this->GetCharWidth($shy_replacement); 06873 // max Y 06874 $maxy = $this->y + $maxh - $h - $this->cell_padding['T'] - $this->cell_padding['B']; 06875 // page width 06876 $pw = $w = $this->w - $this->lMargin - $this->rMargin; 06877 // calculate remaining line width ($w) 06878 if ($this->rtl) { 06879 $w = $this->x - $this->lMargin; 06880 } else { 06881 $w = $this->w - $this->rMargin - $this->x; 06882 } 06883 // max column width 06884 $wmax = $w - $wadj; 06885 if (!$firstline) { 06886 $wmax -= ($this->cell_padding['L'] + $this->cell_padding['R']); 06887 } 06888 if ((!$firstline) AND (($chrwidth > $wmax) OR ($this->GetCharWidth($chars[0]) > $wmax))) { 06889 // a single character do not fit on column 06890 return ''; 06891 } 06892 // minimum row height 06893 $row_height = max($h, $this->FontSize * $this->cell_height_ratio); 06894 $start_page = $this->page; 06895 $i = 0; // character position 06896 $j = 0; // current starting position 06897 $sep = -1; // position of the last blank space 06898 $shy = false; // true if the last blank is a soft hypen (SHY) 06899 $l = 0; // current string length 06900 $nl = 0; //number of lines 06901 $linebreak = false; 06902 $pc = 0; // previous character 06903 // for each character 06904 while ($i < $nb) { 06905 if (($maxh > 0) AND ($this->y >= $maxy) ) { 06906 break; 06907 } 06908 //Get the current character 06909 $c = $chars[$i]; 06910 if ($c == 10) { // 10 = "\n" = new line 06911 //Explicit line break 06912 if ($align == 'J') { 06913 if ($this->rtl) { 06914 $talign = 'R'; 06915 } else { 06916 $talign = 'L'; 06917 } 06918 } else { 06919 $talign = $align; 06920 } 06921 $tmpstr = $this->UniArrSubString($uchars, $j, $i); 06922 if ($firstline) { 06923 $startx = $this->x; 06924 $tmparr = array_slice($chars, $j, ($i - $j)); 06925 if ($rtlmode) { 06926 $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl); 06927 } 06928 $linew = $this->GetArrStringWidth($tmparr); 06929 unset($tmparr); 06930 if ($this->rtl) { 06931 $this->endlinex = $startx - $linew; 06932 } else { 06933 $this->endlinex = $startx + $linew; 06934 } 06935 $w = $linew; 06936 $tmpcellpadding = $this->cell_padding; 06937 if ($maxh == 0) { 06938 $this->SetCellPadding(0); 06939 } 06940 } 06941 if ($firstblock AND $this->isRTLTextDir()) { 06942 $tmpstr = $this->stringRightTrim($tmpstr); 06943 } 06944 // Skip newlines at the begining of a page or column 06945 if (!empty($tmpstr) OR ($this->y < ($this->PageBreakTrigger - $row_height))) { 06946 $this->Cell($w, $h, $tmpstr, 0, 1, $talign, $fill, $link, $stretch); 06947 } 06948 unset($tmpstr); 06949 if ($firstline) { 06950 $this->cell_padding = $tmpcellpadding; 06951 return ($this->UniArrSubString($uchars, $i)); 06952 } 06953 ++$nl; 06954 $j = $i + 1; 06955 $l = 0; 06956 $sep = -1; 06957 $shy = false; 06958 // account for margin changes 06959 if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) { 06960 $this->AcceptPageBreak(); 06961 if ($this->rtl) { 06962 $this->x -= $margin['R']; 06963 } else { 06964 $this->x += $margin['L']; 06965 } 06966 $this->lMargin += $margin['L']; 06967 $this->rMargin += $margin['R']; 06968 } 06969 $w = $this->getRemainingWidth(); 06970 $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R']; 06971 } else { 06972 // 160 is the non-breaking space. 06973 // 173 is SHY (Soft Hypen). 06974 // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator. 06975 // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants. 06976 // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between. 06977 if (($c != 160) AND (($c == 173) OR preg_match($this->re_spaces, $this->unichr($c)))) { 06978 // update last blank space position 06979 $sep = $i; 06980 // check if is a SHY 06981 if ($c == 173) { 06982 $shy = true; 06983 if ($pc == 45) { 06984 $tmp_shy_replacement_width = 0; 06985 $tmp_shy_replacement_char = ''; 06986 } else { 06987 $tmp_shy_replacement_width = $shy_replacement_width; 06988 $tmp_shy_replacement_char = $shy_replacement_char; 06989 } 06990 } else { 06991 $shy = false; 06992 } 06993 } 06994 // update string length 06995 if ($this->isUnicodeFont() AND ($arabic)) { 06996 // with bidirectional algorithm some chars may be changed affecting the line length 06997 // *** very slow *** 06998 $l = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, ($i - $j)), '', $this->tmprtl)); 06999 } else { 07000 $l += $this->GetCharWidth($c); 07001 } 07002 if (($l > $wmax) OR (($c == 173) AND (($l + $tmp_shy_replacement_width) > $wmax)) ) { 07003 // we have reached the end of column 07004 if ($sep == -1) { 07005 // check if the line was already started 07006 if (($this->rtl AND ($this->x <= ($this->w - $this->rMargin - $chrwidth))) 07007 OR ((!$this->rtl) AND ($this->x >= ($this->lMargin + $chrwidth)))) { 07008 // print a void cell and go to next line 07009 $this->Cell($w, $h, '', 0, 1); 07010 $linebreak = true; 07011 if ($firstline) { 07012 return ($this->UniArrSubString($uchars, $j)); 07013 } 07014 } else { 07015 // truncate the word because do not fit on column 07016 $tmpstr = $this->UniArrSubString($uchars, $j, $i); 07017 if ($firstline) { 07018 $startx = $this->x; 07019 $tmparr = array_slice($chars, $j, ($i - $j)); 07020 if ($rtlmode) { 07021 $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl); 07022 } 07023 $linew = $this->GetArrStringWidth($tmparr); 07024 unset($tmparr); 07025 if ($this->rtl) { 07026 $this->endlinex = $startx - $linew; 07027 } else { 07028 $this->endlinex = $startx + $linew; 07029 } 07030 $w = $linew; 07031 $tmpcellpadding = $this->cell_padding; 07032 if ($maxh == 0) { 07033 $this->SetCellPadding(0); 07034 } 07035 } 07036 if ($firstblock AND $this->isRTLTextDir()) { 07037 $tmpstr = $this->stringRightTrim($tmpstr); 07038 } 07039 $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch); 07040 unset($tmpstr); 07041 if ($firstline) { 07042 $this->cell_padding = $tmpcellpadding; 07043 return ($this->UniArrSubString($uchars, $i)); 07044 } 07045 $j = $i; 07046 --$i; 07047 } 07048 } else { 07049 // word wrapping 07050 if ($this->rtl AND (!$firstblock) AND ($sep < $i)) { 07051 $endspace = 1; 07052 } else { 07053 $endspace = 0; 07054 } 07055 // check the length of the next string 07056 $strrest = $this->UniArrSubString($uchars, ($sep + $endspace)); 07057 $nextstr = preg_split('/'.$this->re_space['p'].'/'.$this->re_space['m'], $this->stringTrim($strrest)); 07058 if (isset($nextstr[0]) AND ($this->GetStringWidth($nextstr[0]) > $pw)) { 07059 // truncate the word because do not fit on a full page width 07060 $tmpstr = $this->UniArrSubString($uchars, $j, $i); 07061 if ($firstline) { 07062 $startx = $this->x; 07063 $tmparr = array_slice($chars, $j, ($i - $j)); 07064 if ($rtlmode) { 07065 $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl); 07066 } 07067 $linew = $this->GetArrStringWidth($tmparr); 07068 unset($tmparr); 07069 if ($this->rtl) { 07070 $this->endlinex = $startx - $linew; 07071 } else { 07072 $this->endlinex = $startx + $linew; 07073 } 07074 $w = $linew; 07075 $tmpcellpadding = $this->cell_padding; 07076 if ($maxh == 0) { 07077 $this->SetCellPadding(0); 07078 } 07079 } 07080 if ($firstblock AND $this->isRTLTextDir()) { 07081 $tmpstr = $this->stringRightTrim($tmpstr); 07082 } 07083 $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch); 07084 unset($tmpstr); 07085 if ($firstline) { 07086 $this->cell_padding = $tmpcellpadding; 07087 return ($this->UniArrSubString($uchars, $i)); 07088 } 07089 $j = $i; 07090 --$i; 07091 } else { 07092 // word wrapping 07093 if ($shy) { 07094 // add hypen (minus symbol) at the end of the line 07095 $shy_width = $tmp_shy_replacement_width; 07096 if ($this->rtl) { 07097 $shy_char_left = $tmp_shy_replacement_char; 07098 $shy_char_right = ''; 07099 } else { 07100 $shy_char_left = ''; 07101 $shy_char_right = $tmp_shy_replacement_char; 07102 } 07103 } else { 07104 $shy_width = 0; 07105 $shy_char_left = ''; 07106 $shy_char_right = ''; 07107 } 07108 $tmpstr = $this->UniArrSubString($uchars, $j, ($sep + $endspace)); 07109 if ($firstline) { 07110 $startx = $this->x; 07111 $tmparr = array_slice($chars, $j, (($sep + $endspace) - $j)); 07112 if ($rtlmode) { 07113 $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl); 07114 } 07115 $linew = $this->GetArrStringWidth($tmparr); 07116 unset($tmparr); 07117 if ($this->rtl) { 07118 $this->endlinex = $startx - $linew - $shy_width; 07119 } else { 07120 $this->endlinex = $startx + $linew + $shy_width; 07121 } 07122 $w = $linew; 07123 $tmpcellpadding = $this->cell_padding; 07124 if ($maxh == 0) { 07125 $this->SetCellPadding(0); 07126 } 07127 } 07128 // print the line 07129 if ($firstblock AND $this->isRTLTextDir()) { 07130 $tmpstr = $this->stringRightTrim($tmpstr); 07131 } 07132 $this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch); 07133 unset($tmpstr); 07134 if ($firstline) { 07135 // return the remaining text 07136 $this->cell_padding = $tmpcellpadding; 07137 return ($this->UniArrSubString($uchars, ($sep + $endspace))); 07138 } 07139 $i = $sep; 07140 $sep = -1; 07141 $shy = false; 07142 $j = ($i+1); 07143 } 07144 } 07145 // account for margin changes 07146 if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) { 07147 $this->AcceptPageBreak(); 07148 if ($this->rtl) { 07149 $this->x -= $margin['R']; 07150 } else { 07151 $this->x += $margin['L']; 07152 } 07153 $this->lMargin += $margin['L']; 07154 $this->rMargin += $margin['R']; 07155 } 07156 $w = $this->getRemainingWidth(); 07157 $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R']; 07158 if ($linebreak) { 07159 $linebreak = false; 07160 } else { 07161 ++$nl; 07162 $l = 0; 07163 } 07164 } 07165 } 07166 // save last character 07167 $pc = $c; 07168 ++$i; 07169 } // end while i < nb 07170 // print last substring (if any) 07171 if ($l > 0) { 07172 switch ($align) { 07173 case 'J': 07174 case 'C': { 07175 $w = $w; 07176 break; 07177 } 07178 case 'L': { 07179 if ($this->rtl) { 07180 $w = $w; 07181 } else { 07182 $w = $l; 07183 } 07184 break; 07185 } 07186 case 'R': { 07187 if ($this->rtl) { 07188 $w = $l; 07189 } else { 07190 $w = $w; 07191 } 07192 break; 07193 } 07194 default: { 07195 $w = $l; 07196 break; 07197 } 07198 } 07199 $tmpstr = $this->UniArrSubString($uchars, $j, $nb); 07200 if ($firstline) { 07201 $startx = $this->x; 07202 $tmparr = array_slice($chars, $j, ($nb - $j)); 07203 if ($rtlmode) { 07204 $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl); 07205 } 07206 $linew = $this->GetArrStringWidth($tmparr); 07207 unset($tmparr); 07208 if ($this->rtl) { 07209 $this->endlinex = $startx - $linew; 07210 } else { 07211 $this->endlinex = $startx + $linew; 07212 } 07213 $w = $linew; 07214 $tmpcellpadding = $this->cell_padding; 07215 if ($maxh == 0) { 07216 $this->SetCellPadding(0); 07217 } 07218 } 07219 if ($firstblock AND $this->isRTLTextDir()) { 07220 $tmpstr = $this->stringRightTrim($tmpstr); 07221 } 07222 $this->Cell($w, $h, $tmpstr, 0, $ln, $align, $fill, $link, $stretch); 07223 unset($tmpstr); 07224 if ($firstline) { 07225 $this->cell_padding = $tmpcellpadding; 07226 return ($this->UniArrSubString($uchars, $nb)); 07227 } 07228 ++$nl; 07229 } 07230 if ($firstline) { 07231 return ''; 07232 } 07233 return $nl; 07234 } 07235 07241 protected function getRemainingWidth() { 07242 list($this->x, $this->y) = $this->checkPageRegions(0, $this->x, $this->y); 07243 if ($this->rtl) { 07244 return ($this->x - $this->lMargin); 07245 } else { 07246 return ($this->w - $this->rMargin - $this->x); 07247 } 07248 } 07249 07258 public function UTF8ArrSubString($strarr, $start='', $end='') { 07259 if (strlen($start) == 0) { 07260 $start = 0; 07261 } 07262 if (strlen($end) == 0) { 07263 $end = count($strarr); 07264 } 07265 $string = ''; 07266 for ($i=$start; $i < $end; ++$i) { 07267 $string .= $this->unichr($strarr[$i]); 07268 } 07269 return $string; 07270 } 07271 07281 public function UniArrSubString($uniarr, $start='', $end='') { 07282 if (strlen($start) == 0) { 07283 $start = 0; 07284 } 07285 if (strlen($end) == 0) { 07286 $end = count($uniarr); 07287 } 07288 $string = ''; 07289 for ($i=$start; $i < $end; ++$i) { 07290 $string .= $uniarr[$i]; 07291 } 07292 return $string; 07293 } 07294 07302 public function UTF8ArrayToUniArray($ta) { 07303 return array_map(array($this, 'unichr'), $ta); 07304 } 07305 07314 public function unichr($c) { 07315 if (!$this->isunicode) { 07316 return chr($c); 07317 } elseif ($c <= 0x7F) { 07318 // one byte 07319 return chr($c); 07320 } elseif ($c <= 0x7FF) { 07321 // two bytes 07322 return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F); 07323 } elseif ($c <= 0xFFFF) { 07324 // three bytes 07325 return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F); 07326 } elseif ($c <= 0x10FFFF) { 07327 // four bytes 07328 return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F); 07329 } else { 07330 return ''; 07331 } 07332 } 07333 07341 public function getImageFileType($imgfile, $iminfo=array()) { 07342 $type = ''; 07343 if (isset($iminfo['mime']) AND !empty($iminfo['mime'])) { 07344 $mime = explode('/', $iminfo['mime']); 07345 if ((count($mime) > 1) AND ($mime[0] == 'image') AND (!empty($mime[1]))) { 07346 $type = strtolower(trim($mime[1])); 07347 } 07348 } 07349 if (empty($type)) { 07350 $fileinfo = pathinfo($imgfile); 07351 if (isset($fileinfo['extension']) AND (!$this->empty_string($fileinfo['extension']))) { 07352 $type = strtolower(trim($fileinfo['extension'])); 07353 } 07354 } 07355 if ($type == 'jpg') { 07356 $type = 'jpeg'; 07357 } 07358 return $type; 07359 } 07360 07372 protected function fitBlock($w, $h, $x, $y, $fitonpage=false) { 07373 if ($w <= 0) { 07374 // set maximum width 07375 $w = ($this->w - $this->lMargin - $this->rMargin); 07376 } 07377 if ($h <= 0) { 07378 // set maximum height 07379 $h = ($this->PageBreakTrigger - $this->tMargin); 07380 } 07381 // resize the block to be vertically contained on a single page or single column 07382 if ($fitonpage OR $this->AutoPageBreak) { 07383 $ratio_wh = ($w / $h); 07384 if ($h > ($this->PageBreakTrigger - $this->tMargin)) { 07385 $h = $this->PageBreakTrigger - $this->tMargin; 07386 $w = ($h * $ratio_wh); 07387 } 07388 // resize the block to be horizontally contained on a single page or single column 07389 if ($fitonpage) { 07390 $maxw = ($this->w - $this->lMargin - $this->rMargin); 07391 if ($w > $maxw) { 07392 $w = $maxw; 07393 $h = ($w / $ratio_wh); 07394 } 07395 } 07396 } 07397 // Check whether we need a new page or new column first as this does not fit 07398 $prev_x = $this->x; 07399 $prev_y = $this->y; 07400 if ($this->checkPageBreak($h, $y) OR ($this->y < $prev_y)) { 07401 $y = $this->y; 07402 if ($this->rtl) { 07403 $x += ($prev_x - $this->x); 07404 } else { 07405 $x += ($this->x - $prev_x); 07406 } 07407 $this->newline = true; 07408 } 07409 // resize the block to be contained on the remaining available page or column space 07410 if ($fitonpage) { 07411 $ratio_wh = ($w / $h); 07412 if (($y + $h) > $this->PageBreakTrigger) { 07413 $h = $this->PageBreakTrigger - $y; 07414 $w = ($h * $ratio_wh); 07415 } 07416 if ((!$this->rtl) AND (($x + $w) > ($this->w - $this->rMargin))) { 07417 $w = $this->w - $this->rMargin - $x; 07418 $h = ($w / $ratio_wh); 07419 } elseif (($this->rtl) AND (($x - $w) < ($this->lMargin))) { 07420 $w = $x - $this->lMargin; 07421 $h = ($w / $ratio_wh); 07422 } 07423 } 07424 return array($w, $h, $x, $y); 07425 } 07426 07461 public function Image($file, $x='', $y='', $w=0, $h=0, $type='', $link='', $align='', $resize=false, $dpi=300, $palign='', $ismask=false, $imgmask=false, $border=0, $fitbox=false, $hidden=false, $fitonpage=false, $alt=false, $altimgs=array()) { 07462 if ($x === '') { 07463 $x = $this->x; 07464 } 07465 if ($y === '') { 07466 $y = $this->y; 07467 } 07468 // check page for no-write regions and adapt page margins if necessary 07469 list($x, $y) = $this->checkPageRegions($h, $x, $y); 07470 $cached_file = false; // true when the file is cached 07471 $exurl = ''; // external streams 07472 // check if we are passing an image as file or string 07473 if ($file{0} === '@') { 07474 // image from string 07475 $imgdata = substr($file, 1); 07476 $file = K_PATH_CACHE.'img_'.md5($imgdata); 07477 $fp = fopen($file, 'w'); 07478 fwrite($fp, $imgdata); 07479 fclose($fp); 07480 unset($imgdata); 07481 $cached_file = true; 07482 $imsize = @getimagesize($file); 07483 if ($imsize === FALSE) { 07484 unlink($file); 07485 $cached_file = false; 07486 } 07487 } else { // image file 07488 if ($file{0} === '*') { 07489 // image as external stream 07490 $file = substr($file, 1); 07491 $exurl = $file; 07492 } 07493 // check if is local file 07494 if (!@file_exists($file)) { 07495 // encode spaces on filename (file is probably an URL) 07496 $file = str_replace(' ', '%20', $file); 07497 } 07498 if (@file_exists($file)) { 07499 // get image dimensions 07500 $imsize = @getimagesize($file); 07501 } else { 07502 $imsize = false; 07503 } 07504 if ($imsize === FALSE) { 07505 if (function_exists('curl_init')) { 07506 // try to get remote file data using cURL 07507 $cs = curl_init(); // curl session 07508 curl_setopt($cs, CURLOPT_URL, $file); 07509 curl_setopt($cs, CURLOPT_BINARYTRANSFER, true); 07510 curl_setopt($cs, CURLOPT_FAILONERROR, true); 07511 curl_setopt($cs, CURLOPT_RETURNTRANSFER, true); 07512 curl_setopt($cs, CURLOPT_FOLLOWLOCATION, true); 07513 curl_setopt($cs, CURLOPT_CONNECTTIMEOUT, 5); 07514 curl_setopt($cs, CURLOPT_TIMEOUT, 30); 07515 curl_setopt($cs, CURLOPT_SSL_VERIFYPEER, false); 07516 curl_setopt($cs, CURLOPT_SSL_VERIFYHOST, false); 07517 curl_setopt($cs, CURLOPT_USERAGENT, 'TCPDF'); 07518 $imgdata = curl_exec($cs); 07519 curl_close($cs); 07520 if ($imgdata !== FALSE) { 07521 // copy image to cache 07522 $file = K_PATH_CACHE.'img_'.md5($imgdata); 07523 $fp = fopen($file, 'w'); 07524 fwrite($fp, $imgdata); 07525 fclose($fp); 07526 unset($imgdata); 07527 $cached_file = true; 07528 $imsize = @getimagesize($file); 07529 if ($imsize === FALSE) { 07530 unlink($file); 07531 $cached_file = false; 07532 } 07533 } 07534 } elseif (($w > 0) AND ($h > 0)) { 07535 // get measures from specified data 07536 $pw = $this->getHTMLUnitToUnits($w, 0, $this->pdfunit, true) * $this->imgscale * $this->k; 07537 $ph = $this->getHTMLUnitToUnits($h, 0, $this->pdfunit, true) * $this->imgscale * $this->k; 07538 $imsize = array($pw, $ph); 07539 } 07540 } 07541 } 07542 if ($imsize === FALSE) { 07543 if (substr($file, 0, -34) == K_PATH_CACHE.'msk') { // mask file 07544 // get measures from specified data 07545 $pw = $this->getHTMLUnitToUnits($w, 0, $this->pdfunit, true) * $this->imgscale * $this->k; 07546 $ph = $this->getHTMLUnitToUnits($h, 0, $this->pdfunit, true) * $this->imgscale * $this->k; 07547 $imsize = array($pw, $ph); 07548 } else { 07549 $this->Error('[Image] Unable to get image: '.$file); 07550 } 07551 } 07552 // get original image width and height in pixels 07553 list($pixw, $pixh) = $imsize; 07554 // calculate image width and height on document 07555 if (($w <= 0) AND ($h <= 0)) { 07556 // convert image size to document unit 07557 $w = $this->pixelsToUnits($pixw); 07558 $h = $this->pixelsToUnits($pixh); 07559 } elseif ($w <= 0) { 07560 $w = $h * $pixw / $pixh; 07561 } elseif ($h <= 0) { 07562 $h = $w * $pixh / $pixw; 07563 } elseif (($fitbox !== false) AND ($w > 0) AND ($h > 0)) { 07564 if (strlen($fitbox) !== 2) { 07565 // set default alignment 07566 $fitbox = '--'; 07567 } 07568 // scale image dimensions proportionally to fit within the ($w, $h) box 07569 if ((($w * $pixh) / ($h * $pixw)) < 1) { 07570 // store current height 07571 $oldh = $h; 07572 // calculate new height 07573 $h = $w * $pixh / $pixw; 07574 // height difference 07575 $hdiff = ($oldh - $h); 07576 // vertical alignment 07577 switch (strtoupper($fitbox{1})) { 07578 case 'T': { 07579 break; 07580 } 07581 case 'M': { 07582 $y += ($hdiff / 2); 07583 break; 07584 } 07585 case 'B': { 07586 $y += $hdiff; 07587 break; 07588 } 07589 } 07590 } else { 07591 // store current width 07592 $oldw = $w; 07593 // calculate new width 07594 $w = $h * $pixw / $pixh; 07595 // width difference 07596 $wdiff = ($oldw - $w); 07597 // horizontal alignment 07598 switch (strtoupper($fitbox{0})) { 07599 case 'L': { 07600 if ($this->rtl) { 07601 $x -= $wdiff; 07602 } 07603 break; 07604 } 07605 case 'C': { 07606 if ($this->rtl) { 07607 $x -= ($wdiff / 2); 07608 } else { 07609 $x += ($wdiff / 2); 07610 } 07611 break; 07612 } 07613 case 'R': { 07614 if (!$this->rtl) { 07615 $x += $wdiff; 07616 } 07617 break; 07618 } 07619 } 07620 } 07621 } 07622 // fit the image on available space 07623 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage); 07624 // calculate new minimum dimensions in pixels 07625 $neww = round($w * $this->k * $dpi / $this->dpi); 07626 $newh = round($h * $this->k * $dpi / $this->dpi); 07627 // check if resize is necessary (resize is used only to reduce the image) 07628 $newsize = ($neww * $newh); 07629 $pixsize = ($pixw * $pixh); 07630 if (intval($resize) == 2) { 07631 $resize = true; 07632 } elseif ($newsize >= $pixsize) { 07633 $resize = false; 07634 } 07635 // check if image has been already added on document 07636 $newimage = true; 07637 if (in_array($file, $this->imagekeys)) { 07638 $newimage = false; 07639 // get existing image data 07640 $info = $this->getImageBuffer($file); 07641 if (substr($file, 0, -34) != K_PATH_CACHE.'msk') { 07642 // check if the newer image is larger 07643 $oldsize = ($info['w'] * $info['h']); 07644 if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) { 07645 $newimage = true; 07646 } 07647 } 07648 } elseif (substr($file, 0, -34) != K_PATH_CACHE.'msk') { 07649 // check for cached images with alpha channel 07650 $filehash = md5($file); 07651 $tempfile_plain = K_PATH_CACHE.'mskp_'.$filehash; 07652 $tempfile_alpha = K_PATH_CACHE.'mska_'.$filehash; 07653 if (in_array($tempfile_plain, $this->imagekeys)) { 07654 // get existing image data 07655 $info = $this->getImageBuffer($tempfile_plain); 07656 // check if the newer image is larger 07657 $oldsize = ($info['w'] * $info['h']); 07658 if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) { 07659 $newimage = true; 07660 } else { 07661 $newimage = false; 07662 // embed mask image 07663 $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false); 07664 // embed image, masked with previously embedded mask 07665 return $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask); 07666 } 07667 } 07668 } 07669 if ($newimage) { 07670 //First use of image, get info 07671 $type = strtolower($type); 07672 if ($type == '') { 07673 $type = $this->getImageFileType($file, $imsize); 07674 } elseif ($type == 'jpg') { 07675 $type = 'jpeg'; 07676 } 07677 $mqr = $this->get_mqr(); 07678 $this->set_mqr(false); 07679 // Specific image handlers 07680 $mtd = '_parse'.$type; 07681 // GD image handler function 07682 $gdfunction = 'imagecreatefrom'.$type; 07683 $info = false; 07684 if ((method_exists($this, $mtd)) AND (!($resize AND (function_exists($gdfunction) OR extension_loaded('imagick'))))) { 07685 // TCPDF image functions 07686 $info = $this->$mtd($file); 07687 if ($info == 'pngalpha') { 07688 return $this->ImagePngAlpha($file, $x, $y, $pixw, $pixh, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign, $filehash); 07689 } 07690 } 07691 if (!$info) { 07692 if (function_exists($gdfunction)) { 07693 // GD library 07694 $img = $gdfunction($file); 07695 if ($resize) { 07696 $imgr = imagecreatetruecolor($neww, $newh); 07697 if (($type == 'gif') OR ($type == 'png')) { 07698 $imgr = $this->_setGDImageTransparency($imgr, $img); 07699 } 07700 imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh); 07701 if (($type == 'gif') OR ($type == 'png')) { 07702 $info = $this->_toPNG($imgr); 07703 } else { 07704 $info = $this->_toJPEG($imgr); 07705 } 07706 } else { 07707 if (($type == 'gif') OR ($type == 'png')) { 07708 $info = $this->_toPNG($img); 07709 } else { 07710 $info = $this->_toJPEG($img); 07711 } 07712 } 07713 } elseif (extension_loaded('imagick')) { 07714 // ImageMagick library 07715 $img = new Imagick(); 07716 if ($type == 'SVG') { 07717 // get SVG file content 07718 $svgimg = file_get_contents($file); 07719 // get width and height 07720 $regs = array(); 07721 if (preg_match('/<svg([^>]*)>/si', $svgimg, $regs)) { 07722 $svgtag = $regs[1]; 07723 $tmp = array(); 07724 if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) { 07725 $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false); 07726 $owu = sprintf('%.3F', ($ow * $dpi / 72)).$this->pdfunit; 07727 $svgtag = preg_replace('/[\s]+width[\s]*=[\s]*"[^"]*"/si', ' width="'.$owu.'"', $svgtag, 1); 07728 } else { 07729 $ow = $w; 07730 } 07731 $tmp = array(); 07732 if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) { 07733 $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false); 07734 $ohu = sprintf('%.3F', ($oh * $dpi / 72)).$this->pdfunit; 07735 $svgtag = preg_replace('/[\s]+height[\s]*=[\s]*"[^"]*"/si', ' height="'.$ohu.'"', $svgtag, 1); 07736 } else { 07737 $oh = $h; 07738 } 07739 $tmp = array(); 07740 if (!preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $svgtag, $tmp)) { 07741 $vbw = ($ow * $this->imgscale * $this->k); 07742 $vbh = ($oh * $this->imgscale * $this->k); 07743 $vbox = sprintf(' viewBox="0 0 %.3F %.3F" ', $vbw, $vbh); 07744 $svgtag = $vbox.$svgtag; 07745 } 07746 $svgimg = preg_replace('/<svg([^>]*)>/si', '<svg'.$svgtag.'>', $svgimg, 1); 07747 } 07748 $img->readImageBlob($svgimg); 07749 } else { 07750 $img->readImage($file); 07751 } 07752 if ($resize) { 07753 $img->resizeImage($neww, $newh, 10, 1, false); 07754 } 07755 $img->setCompressionQuality($this->jpeg_quality); 07756 $img->setImageFormat('jpeg'); 07757 $tempname = tempnam(K_PATH_CACHE, 'jpg_'); 07758 $img->writeImage($tempname); 07759 $info = $this->_parsejpeg($tempname); 07760 unlink($tempname); 07761 $img->destroy(); 07762 } else { 07763 return; 07764 } 07765 } 07766 if ($info === false) { 07767 //If false, we cannot process image 07768 return; 07769 } 07770 $this->set_mqr($mqr); 07771 if ($ismask) { 07772 // force grayscale 07773 $info['cs'] = 'DeviceGray'; 07774 } 07775 $info['i'] = $this->numimages; 07776 if (!in_array($file, $this->imagekeys)) { 07777 ++$info['i']; 07778 } 07779 if ($imgmask !== false) { 07780 $info['masked'] = $imgmask; 07781 } 07782 if (!empty($exurl)) { 07783 $info['exurl'] = $exurl; 07784 } 07785 // array of alternative images 07786 $info['altimgs'] = $altimgs; 07787 // add image to document 07788 $this->setImageBuffer($file, $info); 07789 } 07790 if ($cached_file) { 07791 // remove cached file 07792 unlink($file); 07793 } 07794 // set alignment 07795 $this->img_rb_y = $y + $h; 07796 // set alignment 07797 if ($this->rtl) { 07798 if ($palign == 'L') { 07799 $ximg = $this->lMargin; 07800 } elseif ($palign == 'C') { 07801 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 07802 } elseif ($palign == 'R') { 07803 $ximg = $this->w - $this->rMargin - $w; 07804 } else { 07805 $ximg = $x - $w; 07806 } 07807 $this->img_rb_x = $ximg; 07808 } else { 07809 if ($palign == 'L') { 07810 $ximg = $this->lMargin; 07811 } elseif ($palign == 'C') { 07812 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 07813 } elseif ($palign == 'R') { 07814 $ximg = $this->w - $this->rMargin - $w; 07815 } else { 07816 $ximg = $x; 07817 } 07818 $this->img_rb_x = $ximg + $w; 07819 } 07820 if ($ismask OR $hidden) { 07821 // image is not displayed 07822 return $info['i']; 07823 } 07824 $xkimg = $ximg * $this->k; 07825 if (!$alt) { 07826 // only non-alternative immages will be set 07827 $this->_out(sprintf('q %.2F 0 0 %.2F %.2F %.2F cm /I%u Do Q', ($w * $this->k), ($h * $this->k), $xkimg, (($this->h - ($y + $h)) * $this->k), $info['i'])); 07828 } 07829 if (!empty($border)) { 07830 $bx = $this->x; 07831 $by = $this->y; 07832 $this->x = $ximg; 07833 if ($this->rtl) { 07834 $this->x += $w; 07835 } 07836 $this->y = $y; 07837 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true); 07838 $this->x = $bx; 07839 $this->y = $by; 07840 } 07841 if ($link) { 07842 $this->Link($ximg, $y, $w, $h, $link, 0); 07843 } 07844 // set pointer to align the next text/objects 07845 switch($align) { 07846 case 'T': { 07847 $this->y = $y; 07848 $this->x = $this->img_rb_x; 07849 break; 07850 } 07851 case 'M': { 07852 $this->y = $y + round($h/2); 07853 $this->x = $this->img_rb_x; 07854 break; 07855 } 07856 case 'B': { 07857 $this->y = $this->img_rb_y; 07858 $this->x = $this->img_rb_x; 07859 break; 07860 } 07861 case 'N': { 07862 $this->SetY($this->img_rb_y); 07863 break; 07864 } 07865 default:{ 07866 break; 07867 } 07868 } 07869 $this->endlinex = $this->img_rb_x; 07870 if ($this->inxobj) { 07871 // we are inside an XObject template 07872 $this->xobjects[$this->xobjid]['images'][] = $info['i']; 07873 } 07874 return $info['i']; 07875 } 07876 07882 public function set_mqr($mqr) { 07883 if (!defined('PHP_VERSION_ID')) { 07884 $version = PHP_VERSION; 07885 define('PHP_VERSION_ID', (($version{0} * 10000) + ($version{2} * 100) + $version{4})); 07886 } 07887 if (PHP_VERSION_ID < 50300) { 07888 @set_magic_quotes_runtime($mqr); 07889 } 07890 } 07891 07897 public function get_mqr() { 07898 if (!defined('PHP_VERSION_ID')) { 07899 $version = PHP_VERSION; 07900 define('PHP_VERSION_ID', (($version{0} * 10000) + ($version{2} * 100) + $version{4})); 07901 } 07902 if (PHP_VERSION_ID < 50300) { 07903 return @get_magic_quotes_runtime(); 07904 } 07905 return 0; 07906 } 07907 07915 protected function _toJPEG($image) { 07916 $tempname = tempnam(K_PATH_CACHE, 'jpg_'); 07917 imagejpeg($image, $tempname, $this->jpeg_quality); 07918 imagedestroy($image); 07919 $retvars = $this->_parsejpeg($tempname); 07920 // tidy up by removing temporary image 07921 unlink($tempname); 07922 return $retvars; 07923 } 07924 07933 protected function _toPNG($image) { 07934 // set temporary image file name 07935 $tempname = tempnam(K_PATH_CACHE, 'jpg_'); 07936 // turn off interlaced mode 07937 imageinterlace($image, 0); 07938 // create temporary PNG image 07939 imagepng($image, $tempname); 07940 // remove image from memory 07941 imagedestroy($image); 07942 // get PNG image data 07943 $retvars = $this->_parsepng($tempname); 07944 // tidy up by removing temporary image 07945 unlink($tempname); 07946 return $retvars; 07947 } 07948 07957 protected function _setGDImageTransparency($new_image, $image) { 07958 // transparency index 07959 $tid = imagecolortransparent($image); 07960 // default transparency color 07961 $tcol = array('red' => 255, 'green' => 255, 'blue' => 255); 07962 if ($tid >= 0) { 07963 // get the colors for the transparency index 07964 $tcol = imagecolorsforindex($image, $tid); 07965 } 07966 $tid = imagecolorallocate($new_image, $tcol['red'], $tcol['green'], $tcol['blue']); 07967 imagefill($new_image, 0, 0, $tid); 07968 imagecolortransparent($new_image, $tid); 07969 return $new_image; 07970 } 07971 07978 protected function _parsejpeg($file) { 07979 $a = getimagesize($file); 07980 if (empty($a)) { 07981 $this->Error('Missing or incorrect image file: '.$file); 07982 } 07983 if ($a[2] != 2) { 07984 $this->Error('Not a JPEG file: '.$file); 07985 } 07986 // bits per pixel 07987 $bpc = isset($a['bits']) ? intval($a['bits']) : 8; 07988 // number of image channels 07989 if (!isset($a['channels'])) { 07990 $channels = 3; 07991 } else { 07992 $channels = intval($a['channels']); 07993 } 07994 // default colour space 07995 switch ($channels) { 07996 case 1: { 07997 $colspace = 'DeviceGray'; 07998 break; 07999 } 08000 case 3: { 08001 $colspace = 'DeviceRGB'; 08002 break; 08003 } 08004 case 4: { 08005 $colspace = 'DeviceCMYK'; 08006 break; 08007 } 08008 default: { 08009 $channels = 3; 08010 $colspace = 'DeviceRGB'; 08011 break; 08012 } 08013 } 08014 // get file content 08015 $data = file_get_contents($file); 08016 // check for embedded ICC profile 08017 $icc = array(); 08018 $offset = 0; 08019 while (($pos = strpos($data, "ICC_PROFILE\0", $offset)) !== false) { 08020 // get ICC sequence length 08021 $length = ($this->_getUSHORT($data, ($pos - 2)) - 16); 08022 // marker sequence number 08023 $msn = max(1, ord($data{($pos + 12)})); 08024 // number of markers (total of APP2 used) 08025 $nom = max(1, ord($data{($pos + 13)})); 08026 // get sequence segment 08027 $icc[($msn - 1)] = substr($data, ($pos + 14), $length); 08028 // move forward to next sequence 08029 $offset = ($pos + 14 + $length); 08030 } 08031 // order and compact ICC segments 08032 if (count($icc) > 0) { 08033 ksort($icc); 08034 $icc = implode('', $icc); 08035 if ((ord($icc{36}) != 0x61) OR (ord($icc{37}) != 0x63) OR (ord($icc{38}) != 0x73) OR (ord($icc{39}) != 0x70)) { 08036 // invalid ICC profile 08037 $icc = false; 08038 } 08039 } else { 08040 $icc = false; 08041 } 08042 return array('w' => $a[0], 'h' => $a[1], 'ch' => $channels, 'icc' => $icc, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data); 08043 } 08044 08051 protected function _parsepng($file) { 08052 $f = fopen($file, 'rb'); 08053 if ($f === false) { 08054 $this->Error('Can\'t open image file: '.$file); 08055 } 08056 //Check signature 08057 if (fread($f, 8) != chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) { 08058 $this->Error('Not a PNG file: '.$file); 08059 } 08060 //Read header chunk 08061 fread($f, 4); 08062 if (fread($f, 4) != 'IHDR') { 08063 $this->Error('Incorrect PNG file: '.$file); 08064 } 08065 $w = $this->_freadint($f); 08066 $h = $this->_freadint($f); 08067 $bpc = ord(fread($f, 1)); 08068 if ($bpc > 8) { 08069 //$this->Error('16-bit depth not supported: '.$file); 08070 fclose($f); 08071 return false; 08072 } 08073 $ct = ord(fread($f, 1)); 08074 if ($ct == 0) { 08075 $colspace = 'DeviceGray'; 08076 } elseif ($ct == 2) { 08077 $colspace = 'DeviceRGB'; 08078 } elseif ($ct == 3) { 08079 $colspace = 'Indexed'; 08080 } else { 08081 // alpha channel 08082 fclose($f); 08083 return 'pngalpha'; 08084 } 08085 if (ord(fread($f, 1)) != 0) { 08086 //$this->Error('Unknown compression method: '.$file); 08087 fclose($f); 08088 return false; 08089 } 08090 if (ord(fread($f, 1)) != 0) { 08091 //$this->Error('Unknown filter method: '.$file); 08092 fclose($f); 08093 return false; 08094 } 08095 if (ord(fread($f, 1)) != 0) { 08096 //$this->Error('Interlacing not supported: '.$file); 08097 fclose($f); 08098 return false; 08099 } 08100 fread($f, 4); 08101 $channels = ($ct == 2 ? 3 : 1); 08102 $parms = '/DecodeParms << /Predictor 15 /Colors '.$channels.' /BitsPerComponent '.$bpc.' /Columns '.$w.' >>'; 08103 //Scan chunks looking for palette, transparency and image data 08104 $pal = ''; 08105 $trns = ''; 08106 $data = ''; 08107 $icc = false; 08108 do { 08109 $n = $this->_freadint($f); 08110 $type = fread($f, 4); 08111 if ($type == 'PLTE') { 08112 // read palette 08113 $pal = $this->rfread($f, $n); 08114 fread($f, 4); 08115 } elseif ($type == 'tRNS') { 08116 // read transparency info 08117 $t = $this->rfread($f, $n); 08118 if ($ct == 0) { 08119 $trns = array(ord($t{1})); 08120 } elseif ($ct == 2) { 08121 $trns = array(ord($t{1}), ord($t{3}), ord($t{5})); 08122 } else { 08123 $pos = strpos($t, chr(0)); 08124 if ($pos !== false) { 08125 $trns = array($pos); 08126 } 08127 } 08128 fread($f, 4); 08129 } elseif ($type == 'IDAT') { 08130 // read image data block 08131 $data .= $this->rfread($f, $n); 08132 fread($f, 4); 08133 } elseif ($type == 'iCCP') { 08134 // skip profile name and null separator 08135 $len = 0; 08136 while ((ord(fread($f, 1)) > 0) AND ($len < 80)) { 08137 ++$len; 08138 } 08139 // get compression method 08140 if (ord(fread($f, 1)) != 0) { 08141 //$this->Error('Unknown filter method: '.$file); 08142 fclose($f); 08143 return false; 08144 } 08145 // read ICC Color Profile 08146 $icc = $this->rfread($f, ($n - $len - 2)); 08147 // decompress profile 08148 $icc = gzuncompress($icc); 08149 fread($f, 4); 08150 } elseif ($type == 'IEND') { 08151 break; 08152 } else { 08153 $this->rfread($f, $n + 4); 08154 } 08155 } while ($n); 08156 if (($colspace == 'Indexed') AND (empty($pal))) { 08157 //$this->Error('Missing palette in '.$file); 08158 fclose($f); 08159 return false; 08160 } 08161 fclose($f); 08162 return array('w' => $w, 'h' => $h, 'ch' => $channels, 'icc' => $icc, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'parms' => $parms, 'pal' => $pal, 'trns' => $trns, 'data' => $data); 08163 } 08164 08175 protected function rfread($handle, $length) { 08176 $data = fread($handle, $length); 08177 if ($data === false) { 08178 return false; 08179 } 08180 $rest = $length - strlen($data); 08181 if ($rest > 0) { 08182 $data .= $this->rfread($handle, $rest); 08183 } 08184 return $data; 08185 } 08186 08208 protected function ImagePngAlpha($file, $x, $y, $wpx, $hpx, $w, $h, $type, $link, $align, $resize, $dpi, $palign, $filehash='') { 08209 if (empty($filehash)) { 08210 $filehash = md5($file); 08211 } 08212 // create temp image file (without alpha channel) 08213 $tempfile_plain = K_PATH_CACHE.'mskp_'.$filehash; 08214 // create temp alpha file 08215 $tempfile_alpha = K_PATH_CACHE.'mska_'.$filehash; 08216 if (extension_loaded('imagick')) { // ImageMagick extension 08217 // ImageMagick library 08218 $img = new Imagick(); 08219 $img->readImage($file); 08220 // clone image object 08221 $imga = $img->clone(); 08222 // extract alpha channel 08223 $img->separateImageChannel(8); // 8 = (imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE); 08224 $img->negateImage(true); 08225 $img->setImageFormat('png'); 08226 $img->writeImage($tempfile_alpha); 08227 // remove alpha channel 08228 $imga->separateImageChannel(39); // 39 = (imagick::CHANNEL_ALL & ~(imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE)); 08229 $imga->setImageFormat('png'); 08230 $imga->writeImage($tempfile_plain); 08231 } elseif (function_exists('imagecreatefrompng')) { // GD extension 08232 // generate images 08233 $img = imagecreatefrompng($file); 08234 $imgalpha = imagecreate($wpx, $hpx); 08235 // generate gray scale palette (0 -> 255) 08236 for ($c = 0; $c < 256; ++$c) { 08237 ImageColorAllocate($imgalpha, $c, $c, $c); 08238 } 08239 // extract alpha channel 08240 for ($xpx = 0; $xpx < $wpx; ++$xpx) { 08241 for ($ypx = 0; $ypx < $hpx; ++$ypx) { 08242 $color = imagecolorat($img, $xpx, $ypx); 08243 $alpha = ($color >> 24); // shifts off the first 24 bits (where 8x3 are used for each color), and returns the remaining 7 allocated bits (commonly used for alpha) 08244 $alpha = (((127 - $alpha) / 127) * 255); // GD alpha is only 7 bit (0 -> 127) 08245 $alpha = $this->getGDgamma($alpha); // correct gamma 08246 imagesetpixel($imgalpha, $xpx, $ypx, $alpha); 08247 } 08248 } 08249 imagepng($imgalpha, $tempfile_alpha); 08250 imagedestroy($imgalpha); 08251 // extract image without alpha channel 08252 $imgplain = imagecreatetruecolor($wpx, $hpx); 08253 imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx); 08254 imagepng($imgplain, $tempfile_plain); 08255 imagedestroy($imgplain); 08256 } else { 08257 $this->Error('TCPDF requires the Imagick or GD extension to handle PNG images with alpha channel.'); 08258 } 08259 // embed mask image 08260 $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false); 08261 // embed image, masked with previously embedded mask 08262 $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask); 08263 // remove temp files 08264 unlink($tempfile_alpha); 08265 unlink($tempfile_plain); 08266 } 08267 08274 protected function getGDgamma($v) { 08275 return (pow(($v / 255), 2.2) * 255); 08276 } 08277 08287 public function Ln($h='', $cell=false) { 08288 if (($this->num_columns > 1) AND ($this->y == $this->columns[$this->current_column]['y']) AND isset($this->columns[$this->current_column]['x']) AND ($this->x == $this->columns[$this->current_column]['x'])) { 08289 // revove vertical space from the top of the column 08290 return; 08291 } 08292 if ($cell) { 08293 if ($this->rtl) { 08294 $cellpadding = $this->cell_padding['R']; 08295 } else { 08296 $cellpadding = $this->cell_padding['L']; 08297 } 08298 } else { 08299 $cellpadding = 0; 08300 } 08301 if ($this->rtl) { 08302 $this->x = $this->w - $this->rMargin - $cellpadding; 08303 } else { 08304 $this->x = $this->lMargin + $cellpadding; 08305 } 08306 if (is_string($h)) { 08307 $this->y += $this->lasth; 08308 } else { 08309 $this->y += $h; 08310 } 08311 $this->newline = true; 08312 } 08313 08322 public function GetX() { 08323 //Get x position 08324 if ($this->rtl) { 08325 return ($this->w - $this->x); 08326 } else { 08327 return $this->x; 08328 } 08329 } 08330 08338 public function GetAbsX() { 08339 return $this->x; 08340 } 08341 08349 public function GetY() { 08350 return $this->y; 08351 } 08352 08362 public function SetX($x, $rtloff=false) { 08363 if (!$rtloff AND $this->rtl) { 08364 if ($x >= 0) { 08365 $this->x = $this->w - $x; 08366 } else { 08367 $this->x = abs($x); 08368 } 08369 } else { 08370 if ($x >= 0) { 08371 $this->x = $x; 08372 } else { 08373 $this->x = $this->w + $x; 08374 } 08375 } 08376 if ($this->x < 0) { 08377 $this->x = 0; 08378 } 08379 if ($this->x > $this->w) { 08380 $this->x = $this->w; 08381 } 08382 } 08383 08394 public function SetY($y, $resetx=true, $rtloff=false) { 08395 if ($resetx) { 08396 //reset x 08397 if (!$rtloff AND $this->rtl) { 08398 $this->x = $this->w - $this->rMargin; 08399 } else { 08400 $this->x = $this->lMargin; 08401 } 08402 } 08403 if ($y >= 0) { 08404 $this->y = $y; 08405 } else { 08406 $this->y = $this->h + $y; 08407 } 08408 if ($this->y < 0) { 08409 $this->y = 0; 08410 } 08411 if ($this->y > $this->h) { 08412 $this->y = $this->h; 08413 } 08414 } 08415 08426 public function SetXY($x, $y, $rtloff=false) { 08427 $this->SetY($y, false, $rtloff); 08428 $this->SetX($x, $rtloff); 08429 } 08430 08438 protected function sendOutputData($data, $length) { 08439 if (!isset($_SERVER['HTTP_ACCEPT_ENCODING']) OR empty($_SERVER['HTTP_ACCEPT_ENCODING'])) { 08440 // the content length may vary if the server is using compression 08441 header('Content-Length: '.$length); 08442 } 08443 echo $data; 08444 } 08445 08456 public function Output($name='doc.pdf', $dest='I') { 08457 //Output PDF to some destination 08458 //Finish document if necessary 08459 if ($this->state < 3) { 08460 $this->Close(); 08461 } 08462 //Normalize parameters 08463 if (is_bool($dest)) { 08464 $dest = $dest ? 'D' : 'F'; 08465 } 08466 $dest = strtoupper($dest); 08467 if ($dest{0} != 'F') { 08468 $name = preg_replace('/[\s]+/', '_', $name); 08469 $name = preg_replace('/[^a-zA-Z0-9_\.-]/', '', $name); 08470 } 08471 if ($this->sign) { 08472 // *** apply digital signature to the document *** 08473 // get the document content 08474 $pdfdoc = $this->getBuffer(); 08475 // remove last newline 08476 $pdfdoc = substr($pdfdoc, 0, -1); 08477 // Remove the original buffer 08478 if (isset($this->diskcache) AND $this->diskcache) { 08479 // remove buffer file from cache 08480 unlink($this->buffer); 08481 } 08482 unset($this->buffer); 08483 // remove filler space 08484 $byterange_string_len = strlen($this->byterange_string); 08485 // define the ByteRange 08486 $byte_range = array(); 08487 $byte_range[0] = 0; 08488 $byte_range[1] = strpos($pdfdoc, $this->byterange_string) + $byterange_string_len + 10; 08489 $byte_range[2] = $byte_range[1] + $this->signature_max_length + 2; 08490 $byte_range[3] = strlen($pdfdoc) - $byte_range[2]; 08491 $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).substr($pdfdoc, $byte_range[2]); 08492 // replace the ByteRange 08493 $byterange = sprintf('/ByteRange[0 %u %u %u]', $byte_range[1], $byte_range[2], $byte_range[3]); 08494 $byterange .= str_repeat(' ', ($byterange_string_len - strlen($byterange))); 08495 $pdfdoc = str_replace($this->byterange_string, $byterange, $pdfdoc); 08496 // write the document to a temporary folder 08497 $tempdoc = tempnam(K_PATH_CACHE, 'tmppdf_'); 08498 $f = fopen($tempdoc, 'wb'); 08499 if (!$f) { 08500 $this->Error('Unable to create temporary file: '.$tempdoc); 08501 } 08502 $pdfdoc_length = strlen($pdfdoc); 08503 fwrite($f, $pdfdoc, $pdfdoc_length); 08504 fclose($f); 08505 // get digital signature via openssl library 08506 $tempsign = tempnam(K_PATH_CACHE, 'tmpsig_'); 08507 if (empty($this->signature_data['extracerts'])) { 08508 openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED); 08509 } else { 08510 openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED, $this->signature_data['extracerts']); 08511 } 08512 unlink($tempdoc); 08513 // read signature 08514 $signature = file_get_contents($tempsign); 08515 unlink($tempsign); 08516 // extract signature 08517 $signature = substr($signature, $pdfdoc_length); 08518 $signature = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13)); 08519 $tmparr = explode("\n\n", $signature); 08520 $signature = $tmparr[1]; 08521 unset($tmparr); 08522 // decode signature 08523 $signature = base64_decode(trim($signature)); 08524 // convert signature to hex 08525 $signature = current(unpack('H*', $signature)); 08526 $signature = str_pad($signature, $this->signature_max_length, '0'); 08527 // disable disk caching 08528 $this->diskcache = false; 08529 // Add signature to the document 08530 $this->buffer = substr($pdfdoc, 0, $byte_range[1]).'<'.$signature.'>'.substr($pdfdoc, $byte_range[1]); 08531 $this->bufferlen = strlen($this->buffer); 08532 } 08533 switch($dest) { 08534 case 'I': { 08535 // Send PDF to the standard output 08536 if (ob_get_contents()) { 08537 $this->Error('Some data has already been output, can\'t send PDF file'); 08538 } 08539 if (php_sapi_name() != 'cli') { 08540 // send output to a browser 08541 header('Content-Type: application/pdf'); 08542 if (headers_sent()) { 08543 $this->Error('Some data has already been output to browser, can\'t send PDF file'); 08544 } 08545 header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1 08546 header('Pragma: public'); 08547 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past 08548 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); 08549 header('Content-Disposition: inline; filename="'.basename($name).'";'); 08550 $this->sendOutputData($this->getBuffer(), $this->bufferlen); 08551 } else { 08552 echo $this->getBuffer(); 08553 } 08554 break; 08555 } 08556 case 'D': { 08557 // download PDF as file 08558 if (ob_get_contents()) { 08559 $this->Error('Some data has already been output, can\'t send PDF file'); 08560 } 08561 header('Content-Description: File Transfer'); 08562 if (headers_sent()) { 08563 $this->Error('Some data has already been output to browser, can\'t send PDF file'); 08564 } 08565 header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1 08566 header('Pragma: public'); 08567 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past 08568 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); 08569 // force download dialog 08570 if (strpos(php_sapi_name(), 'cgi') === false) { 08571 header('Content-Type: application/force-download'); 08572 header('Content-Type: application/octet-stream', false); 08573 header('Content-Type: application/download', false); 08574 header('Content-Type: application/pdf', false); 08575 } else { 08576 header('Content-Type: application/pdf'); 08577 } 08578 // use the Content-Disposition header to supply a recommended filename 08579 header('Content-Disposition: attachment; filename="'.basename($name).'";'); 08580 header('Content-Transfer-Encoding: binary'); 08581 $this->sendOutputData($this->getBuffer(), $this->bufferlen); 08582 break; 08583 } 08584 case 'F': 08585 case 'FI': 08586 case 'FD': { 08587 // save PDF to a local file 08588 if ($this->diskcache) { 08589 copy($this->buffer, $name); 08590 } else { 08591 $f = fopen($name, 'wb'); 08592 if (!$f) { 08593 $this->Error('Unable to create output file: '.$name); 08594 } 08595 fwrite($f, $this->getBuffer(), $this->bufferlen); 08596 fclose($f); 08597 } 08598 if ($dest == 'FI') { 08599 // send headers to browser 08600 header('Content-Type: application/pdf'); 08601 header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1 08602 header('Pragma: public'); 08603 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past 08604 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); 08605 header('Content-Disposition: inline; filename="'.basename($name).'";'); 08606 $this->sendOutputData(file_get_contents($name), filesize($name)); 08607 } elseif ($dest == 'FD') { 08608 // send headers to browser 08609 if (ob_get_contents()) { 08610 $this->Error('Some data has already been output, can\'t send PDF file'); 08611 } 08612 header('Content-Description: File Transfer'); 08613 if (headers_sent()) { 08614 $this->Error('Some data has already been output to browser, can\'t send PDF file'); 08615 } 08616 header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1 08617 header('Pragma: public'); 08618 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past 08619 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); 08620 // force download dialog 08621 if (strpos(php_sapi_name(), 'cgi') === false) { 08622 header('Content-Type: application/force-download'); 08623 header('Content-Type: application/octet-stream', false); 08624 header('Content-Type: application/download', false); 08625 header('Content-Type: application/pdf', false); 08626 } else { 08627 header('Content-Type: application/pdf'); 08628 } 08629 // use the Content-Disposition header to supply a recommended filename 08630 header('Content-Disposition: attachment; filename="'.basename($name).'";'); 08631 header('Content-Transfer-Encoding: binary'); 08632 $this->sendOutputData(file_get_contents($name), filesize($name)); 08633 } 08634 break; 08635 } 08636 case 'E': { 08637 // return PDF as base64 mime multi-part email attachment (RFC 2045) 08638 $retval = 'Content-Type: application/pdf;'."\r\n"; 08639 $retval .= ' name="'.$name.'"'."\r\n"; 08640 $retval .= 'Content-Transfer-Encoding: base64'."\r\n"; 08641 $retval .= 'Content-Disposition: attachment;'."\r\n"; 08642 $retval .= ' filename="'.$name.'"'."\r\n\r\n"; 08643 $retval .= chunk_split(base64_encode($this->getBuffer()), 76, "\r\n"); 08644 return $retval; 08645 } 08646 case 'S': { 08647 // returns PDF as a string 08648 return $this->getBuffer(); 08649 } 08650 default: { 08651 $this->Error('Incorrect output destination: '.$dest); 08652 } 08653 } 08654 return ''; 08655 } 08656 08664 public function _destroy($destroyall=false, $preserve_objcopy=false) { 08665 if ($destroyall AND isset($this->diskcache) AND $this->diskcache AND (!$preserve_objcopy) AND (!$this->empty_string($this->buffer))) { 08666 // remove buffer file from cache 08667 unlink($this->buffer); 08668 } 08669 foreach (array_keys(get_object_vars($this)) as $val) { 08670 if ($destroyall OR ( 08671 ($val != 'internal_encoding') 08672 AND ($val != 'state') 08673 AND ($val != 'bufferlen') 08674 AND ($val != 'buffer') 08675 AND ($val != 'diskcache') 08676 AND ($val != 'sign') 08677 AND ($val != 'signature_data') 08678 AND ($val != 'signature_max_length') 08679 AND ($val != 'byterange_string') 08680 )) { 08681 if ((!$preserve_objcopy OR ($val != 'objcopy')) AND isset($this->$val)) { 08682 unset($this->$val); 08683 } 08684 } 08685 } 08686 } 08687 08692 protected function _dochecks() { 08693 //Check for locale-related bug 08694 if (1.1 == 1) { 08695 $this->Error('Don\'t alter the locale before including class file'); 08696 } 08697 //Check for decimal separator 08698 if (sprintf('%.1F', 1.0) != '1.0') { 08699 setlocale(LC_NUMERIC, 'C'); 08700 } 08701 } 08702 08708 protected function _getfontpath() { 08709 if (!defined('K_PATH_FONTS') AND is_dir(dirname(__FILE__).'/fonts')) { 08710 define('K_PATH_FONTS', dirname(__FILE__).'/fonts/'); 08711 } 08712 return defined('K_PATH_FONTS') ? K_PATH_FONTS : ''; 08713 } 08714 08721 protected function getInternalPageNumberAliases($a= '') { 08722 $alias = array(); 08723 // build array of Unicode + ASCII variants (the order is important) 08724 $alias = array('u' => array(), 'a' => array()); 08725 $u = '{'.$a.'}'; 08726 $alias['u'][] = $this->_escape($u); 08727 if ($this->isunicode) { 08728 $alias['u'][] = $this->_escape($this->UTF8ToLatin1($u)); 08729 $alias['u'][] = $this->_escape($this->utf8StrRev($u, false, $this->tmprtl)); 08730 $alias['a'][] = $this->_escape($this->UTF8ToLatin1($a)); 08731 $alias['a'][] = $this->_escape($this->utf8StrRev($a, false, $this->tmprtl)); 08732 } 08733 $alias['a'][] = $this->_escape($a); 08734 return $alias; 08735 } 08736 08742 protected function getAllInternalPageNumberAliases() { 08743 $basic_alias = array($this->alias_tot_pages, $this->alias_num_page, $this->alias_group_tot_pages, $this->alias_group_num_page, $this->alias_right_shift); 08744 $pnalias = array(); 08745 foreach($basic_alias as $k => $a) { 08746 $pnalias[$k] = $this->getInternalPageNumberAliases($a); 08747 } 08748 return $pnalias; 08749 } 08750 08759 protected function replacePageNumAliases($page, $replace, $diff=0) { 08760 foreach ($replace as $rep) { 08761 foreach ($rep[3] as $a) { 08762 if (strpos($page, $a) !== false) { 08763 $page = str_replace($a, $rep[0], $page); 08764 $diff += ($rep[2] - $rep[1]); 08765 } 08766 } 08767 } 08768 return array($page, $diff); 08769 } 08770 08780 protected function replaceRightShiftPageNumAliases($page, $aliases, $diff) { 08781 foreach ($aliases as $type => $alias) { 08782 foreach ($alias as $a) { 08783 // find position of compensation factor 08784 $startnum = (strpos($a, ':') + 1); 08785 $a = substr($a, 0, $startnum); 08786 if (($pos = strpos($page, $a)) !== false) { 08787 // end of alias 08788 $endnum = strpos($page, '}', $pos); 08789 // string to be replaced 08790 $aa = substr($page, $pos, ($endnum - $pos + 1)); 08791 // get compensation factor 08792 $ratio = substr($page, ($pos + $startnum), ($endnum - $pos - $startnum)); 08793 $ratio = preg_replace('/[^0-9\.]/', '', $ratio); 08794 $ratio = floatval($ratio); 08795 if ($type == 'u') { 08796 $chrdiff = floor(($diff + 12) * $ratio); 08797 $shift = str_repeat(' ', $chrdiff); 08798 $shift = $this->UTF8ToUTF16BE($shift, false); 08799 } else { 08800 $chrdiff = floor(($diff + 11) * $ratio); 08801 $shift = str_repeat(' ', $chrdiff); 08802 } 08803 $page = str_replace($aa, $shift, $page); 08804 } 08805 } 08806 } 08807 return $page; 08808 } 08809 08814 protected function _putpages() { 08815 $filter = ($this->compress) ? '/Filter /FlateDecode ' : ''; 08816 // get internal aliases for page numbers 08817 $pnalias = $this->getAllInternalPageNumberAliases(); 08818 $num_pages = $this->numpages; 08819 $ptpa = $this->formatPageNumber(($this->starting_page_number + $num_pages - 1)); 08820 $ptpu = $this->UTF8ToUTF16BE($ptpa, false); 08821 $ptp_num_chars = $this->GetNumChars($ptpa); 08822 $pagegroupnum = 0; 08823 $groupnum = 0; 08824 $ptgu = 1; 08825 $ptga = 1; 08826 for ($n = 1; $n <= $num_pages; ++$n) { 08827 // get current page 08828 $temppage = $this->getPageBuffer($n); 08829 $pagelen = strlen($temppage); 08830 // set replacements for total pages number 08831 $pnpa = $this->formatPageNumber(($this->starting_page_number + $n - 1)); 08832 $pnpu = $this->UTF8ToUTF16BE($pnpa, false); 08833 $pnp_num_chars = $this->GetNumChars($pnpa); 08834 $pdiff = 0; // difference used for right shift alignment of page numbers 08835 $gdiff = 0; // difference used for right shift alignment of page group numbers 08836 if (!empty($this->pagegroups)) { 08837 if (isset($this->newpagegroup[$n])) { 08838 $pagegroupnum = 0; 08839 ++$groupnum; 08840 $ptga = $this->formatPageNumber($this->pagegroups[$groupnum]); 08841 $ptgu = $this->UTF8ToUTF16BE($ptga, false); 08842 $ptg_num_chars = $this->GetNumChars($ptga); 08843 } 08844 ++$pagegroupnum; 08845 $pnga = $this->formatPageNumber($pagegroupnum); 08846 $pngu = $this->UTF8ToUTF16BE($pnga, false); 08847 $png_num_chars = $this->GetNumChars($pnga); 08848 // replace page numbers 08849 $replace = array(); 08850 $replace[] = array($ptgu, $ptg_num_chars, 9, $pnalias[2]['u']); 08851 $replace[] = array($ptga, $ptg_num_chars, 7, $pnalias[2]['a']); 08852 $replace[] = array($pngu, $png_num_chars, 9, $pnalias[3]['u']); 08853 $replace[] = array($pnga, $png_num_chars, 7, $pnalias[3]['a']); 08854 list($temppage, $gdiff) = $this->replacePageNumAliases($temppage, $replace, $gdiff); 08855 } 08856 // replace page numbers 08857 $replace = array(); 08858 $replace[] = array($ptpu, $ptp_num_chars, 9, $pnalias[0]['u']); 08859 $replace[] = array($ptpa, $ptp_num_chars, 7, $pnalias[0]['a']); 08860 $replace[] = array($pnpu, $pnp_num_chars, 9, $pnalias[1]['u']); 08861 $replace[] = array($pnpa, $pnp_num_chars, 7, $pnalias[1]['a']); 08862 list($temppage, $pdiff) = $this->replacePageNumAliases($temppage, $replace, $pdiff); 08863 // replace right shift alias 08864 $temppage = $this->replaceRightShiftPageNumAliases($temppage, $pnalias[4], max($pdiff, $gdiff)); 08865 // replace EPS marker 08866 $temppage = str_replace($this->epsmarker, '', $temppage); 08867 //Page 08868 $this->page_obj_id[$n] = $this->_newobj(); 08869 $out = '<<'; 08870 $out .= ' /Type /Page'; 08871 $out .= ' /Parent 1 0 R'; 08872 $out .= ' /LastModified '.$this->_datestring(); 08873 $out .= ' /Resources 2 0 R'; 08874 $boxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox'); 08875 foreach ($boxes as $box) { 08876 $out .= ' /'.$box; 08877 $out .= sprintf(' [%.2F %.2F %.2F %.2F]', $this->pagedim[$n][$box]['llx'], $this->pagedim[$n][$box]['lly'], $this->pagedim[$n][$box]['urx'], $this->pagedim[$n][$box]['ury']); 08878 } 08879 if (isset($this->pagedim[$n]['BoxColorInfo']) AND !empty($this->pagedim[$n]['BoxColorInfo'])) { 08880 $out .= ' /BoxColorInfo <<'; 08881 foreach ($boxes as $box) { 08882 if (isset($this->pagedim[$n]['BoxColorInfo'][$box])) { 08883 $out .= ' /'.$box.' <<'; 08884 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['C'])) { 08885 $color = $this->pagedim[$n]['BoxColorInfo'][$box]['C']; 08886 $out .= ' /C ['; 08887 $out .= sprintf(' %.3F %.3F %.3F', $color[0]/255, $color[1]/255, $color[2]/255); 08888 $out .= ' ]'; 08889 } 08890 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['W'])) { 08891 $out .= ' /W '.($this->pagedim[$n]['BoxColorInfo'][$box]['W'] * $this->k); 08892 } 08893 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['S'])) { 08894 $out .= ' /S /'.$this->pagedim[$n]['BoxColorInfo'][$box]['S']; 08895 } 08896 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['D'])) { 08897 $dashes = $this->pagedim[$n]['BoxColorInfo'][$box]['D']; 08898 $out .= ' /D ['; 08899 foreach ($dashes as $dash) { 08900 $out .= sprintf(' %.3F', ($dash * $this->k)); 08901 } 08902 $out .= ' ]'; 08903 } 08904 $out .= ' >>'; 08905 } 08906 } 08907 $out .= ' >>'; 08908 } 08909 $out .= ' /Contents '.($this->n + 1).' 0 R'; 08910 $out .= ' /Rotate '.$this->pagedim[$n]['Rotate']; 08911 if (!$this->pdfa_mode) { 08912 $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceRGB >>'; 08913 } 08914 if (isset($this->pagedim[$n]['trans']) AND !empty($this->pagedim[$n]['trans'])) { 08915 // page transitions 08916 if (isset($this->pagedim[$n]['trans']['Dur'])) { 08917 $out .= ' /Dur '.$this->pagedim[$n]['trans']['Dur']; 08918 } 08919 $out .= ' /Trans <<'; 08920 $out .= ' /Type /Trans'; 08921 if (isset($this->pagedim[$n]['trans']['S'])) { 08922 $out .= ' /S /'.$this->pagedim[$n]['trans']['S']; 08923 } 08924 if (isset($this->pagedim[$n]['trans']['D'])) { 08925 $out .= ' /D '.$this->pagedim[$n]['trans']['D']; 08926 } 08927 if (isset($this->pagedim[$n]['trans']['Dm'])) { 08928 $out .= ' /Dm /'.$this->pagedim[$n]['trans']['Dm']; 08929 } 08930 if (isset($this->pagedim[$n]['trans']['M'])) { 08931 $out .= ' /M /'.$this->pagedim[$n]['trans']['M']; 08932 } 08933 if (isset($this->pagedim[$n]['trans']['Di'])) { 08934 $out .= ' /Di '.$this->pagedim[$n]['trans']['Di']; 08935 } 08936 if (isset($this->pagedim[$n]['trans']['SS'])) { 08937 $out .= ' /SS '.$this->pagedim[$n]['trans']['SS']; 08938 } 08939 if (isset($this->pagedim[$n]['trans']['B'])) { 08940 $out .= ' /B '.$this->pagedim[$n]['trans']['B']; 08941 } 08942 $out .= ' >>'; 08943 } 08944 $out .= $this->_getannotsrefs($n); 08945 $out .= ' /PZ '.$this->pagedim[$n]['PZ']; 08946 $out .= ' >>'; 08947 $out .= "\n".'endobj'; 08948 $this->_out($out); 08949 //Page content 08950 $p = ($this->compress) ? gzcompress($temppage) : $temppage; 08951 $this->_newobj(); 08952 $p = $this->_getrawstream($p); 08953 $this->_out('<<'.$filter.'/Length '.strlen($p).'>> stream'."\n".$p."\n".'endstream'."\n".'endobj'); 08954 if ($this->diskcache) { 08955 // remove temporary files 08956 unlink($this->pages[$n]); 08957 } 08958 } 08959 //Pages root 08960 $out = $this->_getobj(1)."\n"; 08961 $out .= '<< /Type /Pages /Kids ['; 08962 foreach($this->page_obj_id as $page_obj) { 08963 $out .= ' '.$page_obj.' 0 R'; 08964 } 08965 $out .= ' ] /Count '.$num_pages.' >>'; 08966 $out .= "\n".'endobj'; 08967 $this->_out($out); 08968 } 08969 08978 protected function _putannotsrefs($n) { 08979 $this->_out($this->_getannotsrefs($n)); 08980 } 08981 08990 protected function _getannotsrefs($n) { 08991 if (!(isset($this->PageAnnots[$n]) OR ($this->sign AND isset($this->signature_data['cert_type'])))) { 08992 return ''; 08993 } 08994 $out = ' /Annots ['; 08995 if (isset($this->PageAnnots[$n])) { 08996 foreach ($this->PageAnnots[$n] as $key => $val) { 08997 if (!in_array($val['n'], $this->radio_groups)) { 08998 $out .= ' '.$val['n'].' 0 R'; 08999 } 09000 } 09001 // add radiobutton groups 09002 if (isset($this->radiobutton_groups[$n])) { 09003 foreach ($this->radiobutton_groups[$n] as $key => $data) { 09004 if (isset($data['n'])) { 09005 $out .= ' '.$data['n'].' 0 R'; 09006 } 09007 } 09008 } 09009 } 09010 if ($this->sign AND ($n == $this->signature_appearance['page']) AND isset($this->signature_data['cert_type'])) { 09011 // set reference for signature object 09012 $out .= ' '.$this->sig_obj_id.' 0 R'; 09013 } 09014 if (!empty($this->empty_signature_appearance)) { 09015 foreach ($this->empty_signature_appearance as $esa) { 09016 if ($esa['page'] == $n) { 09017 // set reference for empty signature objects 09018 $out .= ' '.$esa['objid'].' 0 R'; 09019 } 09020 } 09021 } 09022 $out .= ' ]'; 09023 return $out; 09024 } 09025 09034 protected function _putannotsobjs() { 09035 // reset object counter 09036 for ($n=1; $n <= $this->numpages; ++$n) { 09037 if (isset($this->PageAnnots[$n])) { 09038 // set page annotations 09039 foreach ($this->PageAnnots[$n] as $key => $pl) { 09040 $annot_obj_id = $this->PageAnnots[$n][$key]['n']; 09041 // create annotation object for grouping radiobuttons 09042 if (isset($this->radiobutton_groups[$n][$pl['txt']]) AND is_array($this->radiobutton_groups[$n][$pl['txt']])) { 09043 $radio_button_obj_id = $this->radiobutton_groups[$n][$pl['txt']]['n']; 09044 $annots = '<<'; 09045 $annots .= ' /Type /Annot'; 09046 $annots .= ' /Subtype /Widget'; 09047 $annots .= ' /Rect [0 0 0 0]'; 09048 if ($this->radiobutton_groups[$n][$pl['txt']]['#readonly#']) { 09049 // read only 09050 $annots .= ' /F 68'; 09051 $annots .= ' /Ff 49153'; 09052 } else { 09053 $annots .= ' /F 4'; // default print for PDF/A 09054 $annots .= ' /Ff 49152'; 09055 } 09056 $annots .= ' /T '.$this->_datastring($pl['txt'], $radio_button_obj_id); 09057 $annots .= ' /FT /Btn'; 09058 $annots .= ' /Kids ['; 09059 foreach ($this->radiobutton_groups[$n][$pl['txt']] as $key => $data) { 09060 if (isset($data['kid'])) { 09061 $annots .= ' '.$data['kid'].' 0 R'; 09062 if ($data['def'] !== 'Off') { 09063 $defval = $data['def']; 09064 } 09065 } 09066 } 09067 $annots .= ' ]'; 09068 if (isset($defval)) { 09069 $annots .= ' /V /'.$defval; 09070 } 09071 $annots .= ' >>'; 09072 $this->_out($this->_getobj($radio_button_obj_id)."\n".$annots."\n".'endobj'); 09073 $this->form_obj_id[] = $radio_button_obj_id; 09074 // store object id to be used on Parent entry of Kids 09075 $this->radiobutton_groups[$n][$pl['txt']] = $radio_button_obj_id; 09076 } 09077 $formfield = false; 09078 $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER); 09079 $a = $pl['x'] * $this->k; 09080 $b = $this->pagedim[$n]['h'] - (($pl['y'] + $pl['h']) * $this->k); 09081 $c = $pl['w'] * $this->k; 09082 $d = $pl['h'] * $this->k; 09083 $rect = sprintf('%.2F %.2F %.2F %.2F', $a, $b, $a+$c, $b+$d); 09084 // create new annotation object 09085 $annots = '<</Type /Annot'; 09086 $annots .= ' /Subtype /'.$pl['opt']['subtype']; 09087 $annots .= ' /Rect ['.$rect.']'; 09088 $ft = array('Btn', 'Tx', 'Ch', 'Sig'); 09089 if (isset($pl['opt']['ft']) AND in_array($pl['opt']['ft'], $ft)) { 09090 $annots .= ' /FT /'.$pl['opt']['ft']; 09091 $formfield = true; 09092 } 09093 $annots .= ' /Contents '.$this->_textstring($pl['txt'], $annot_obj_id); 09094 $annots .= ' /P '.$this->page_obj_id[$n].' 0 R'; 09095 $annots .= ' /NM '.$this->_datastring(sprintf('%04u-%04u', $n, $key), $annot_obj_id); 09096 $annots .= ' /M '.$this->_datestring($annot_obj_id); 09097 if (isset($pl['opt']['f'])) { 09098 $fval = 0; 09099 if (is_array($pl['opt']['f'])) { 09100 foreach ($pl['opt']['f'] as $f) { 09101 switch (strtolower($f)) { 09102 case 'invisible': { 09103 $fval += 1 << 0; 09104 break; 09105 } 09106 case 'hidden': { 09107 $fval += 1 << 1; 09108 break; 09109 } 09110 case 'print': { 09111 $fval += 1 << 2; 09112 break; 09113 } 09114 case 'nozoom': { 09115 $fval += 1 << 3; 09116 break; 09117 } 09118 case 'norotate': { 09119 $fval += 1 << 4; 09120 break; 09121 } 09122 case 'noview': { 09123 $fval += 1 << 5; 09124 break; 09125 } 09126 case 'readonly': { 09127 $fval += 1 << 6; 09128 break; 09129 } 09130 case 'locked': { 09131 $fval += 1 << 8; 09132 break; 09133 } 09134 case 'togglenoview': { 09135 $fval += 1 << 9; 09136 break; 09137 } 09138 case 'lockedcontents': { 09139 $fval += 1 << 10; 09140 break; 09141 } 09142 default: { 09143 break; 09144 } 09145 } 09146 } 09147 } else { 09148 $fval = intval($pl['opt']['f']); 09149 } 09150 } else { 09151 $fval = 4; 09152 } 09153 if ($this->pdfa_mode) { 09154 // force print flag for PDF/A mode 09155 $fval |= 4; 09156 } 09157 $annots .= ' /F '.intval($fval); 09158 if (isset($pl['opt']['as']) AND is_string($pl['opt']['as'])) { 09159 $annots .= ' /AS /'.$pl['opt']['as']; 09160 } 09161 if (isset($pl['opt']['ap'])) { 09162 // appearance stream 09163 $annots .= ' /AP <<'; 09164 if (is_array($pl['opt']['ap'])) { 09165 foreach ($pl['opt']['ap'] as $apmode => $apdef) { 09166 // $apmode can be: n = normal; r = rollover; d = down; 09167 $annots .= ' /'.strtoupper($apmode); 09168 if (is_array($apdef)) { 09169 $annots .= ' <<'; 09170 foreach ($apdef as $apstate => $stream) { 09171 // reference to XObject that define the appearance for this mode-state 09172 $apsobjid = $this->_putAPXObject($c, $d, $stream); 09173 $annots .= ' /'.$apstate.' '.$apsobjid.' 0 R'; 09174 } 09175 $annots .= ' >>'; 09176 } else { 09177 // reference to XObject that define the appearance for this mode 09178 $apsobjid = $this->_putAPXObject($c, $d, $apdef); 09179 $annots .= ' '.$apsobjid.' 0 R'; 09180 } 09181 } 09182 } else { 09183 $annots .= $pl['opt']['ap']; 09184 } 09185 $annots .= ' >>'; 09186 } 09187 if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) { 09188 $annots .= ' /BS <<'; 09189 $annots .= ' /Type /Border'; 09190 if (isset($pl['opt']['bs']['w'])) { 09191 $annots .= ' /W '.intval($pl['opt']['bs']['w']); 09192 } 09193 $bstyles = array('S', 'D', 'B', 'I', 'U'); 09194 if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $bstyles)) { 09195 $annots .= ' /S /'.$pl['opt']['bs']['s']; 09196 } 09197 if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) { 09198 $annots .= ' /D ['; 09199 foreach ($pl['opt']['bs']['d'] as $cord) { 09200 $annots .= ' '.intval($cord); 09201 } 09202 $annots .= ']'; 09203 } 09204 $annots .= ' >>'; 09205 } else { 09206 $annots .= ' /Border ['; 09207 if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) { 09208 $annots .= intval($pl['opt']['border'][0]).' '; 09209 $annots .= intval($pl['opt']['border'][1]).' '; 09210 $annots .= intval($pl['opt']['border'][2]); 09211 if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) { 09212 $annots .= ' ['; 09213 foreach ($pl['opt']['border'][3] as $dash) { 09214 $annots .= intval($dash).' '; 09215 } 09216 $annots .= ']'; 09217 } 09218 } else { 09219 $annots .= '0 0 0'; 09220 } 09221 $annots .= ']'; 09222 } 09223 if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) { 09224 $annots .= ' /BE <<'; 09225 $bstyles = array('S', 'C'); 09226 if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $markups)) { 09227 $annots .= ' /S /'.$pl['opt']['bs']['s']; 09228 } else { 09229 $annots .= ' /S /S'; 09230 } 09231 if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) { 09232 $annots .= ' /I '.sprintf(' %.4F', $pl['opt']['be']['i']); 09233 } 09234 $annots .= '>>'; 09235 } 09236 if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c'])) AND !empty($pl['opt']['c'])) { 09237 $annots .= ' /C ['; 09238 foreach ($pl['opt']['c'] as $col) { 09239 $col = intval($col); 09240 $color = $col <= 0 ? 0 : ($col >= 255 ? 1 : $col / 255); 09241 $annots .= sprintf(' %.4F', $color); 09242 } 09243 $annots .= ']'; 09244 } 09245 //$annots .= ' /StructParent '; 09246 //$annots .= ' /OC '; 09247 $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight', 'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound'); 09248 if (in_array(strtolower($pl['opt']['subtype']), $markups)) { 09249 // this is a markup type 09250 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) { 09251 $annots .= ' /T '.$this->_textstring($pl['opt']['t'], $annot_obj_id); 09252 } 09253 //$annots .= ' /Popup '; 09254 if (isset($pl['opt']['ca'])) { 09255 $annots .= ' /CA '.sprintf('%.4F', floatval($pl['opt']['ca'])); 09256 } 09257 if (isset($pl['opt']['rc'])) { 09258 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id); 09259 } 09260 $annots .= ' /CreationDate '.$this->_datestring($annot_obj_id); 09261 //$annots .= ' /IRT '; 09262 if (isset($pl['opt']['subj'])) { 09263 $annots .= ' /Subj '.$this->_textstring($pl['opt']['subj'], $annot_obj_id); 09264 } 09265 //$annots .= ' /RT '; 09266 //$annots .= ' /IT '; 09267 //$annots .= ' /ExData '; 09268 } 09269 $lineendings = array('Square', 'Circle', 'Diamond', 'OpenArrow', 'ClosedArrow', 'None', 'Butt', 'ROpenArrow', 'RClosedArrow', 'Slash'); 09270 // Annotation types 09271 switch (strtolower($pl['opt']['subtype'])) { 09272 case 'text': { 09273 if (isset($pl['opt']['open'])) { 09274 $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false'); 09275 } 09276 $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph'); 09277 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) { 09278 $annots .= ' /Name /'.$pl['opt']['name']; 09279 } else { 09280 $annots .= ' /Name /Note'; 09281 } 09282 $statemodels = array('Marked', 'Review'); 09283 if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) { 09284 $annots .= ' /StateModel /'.$pl['opt']['statemodel']; 09285 } else { 09286 $pl['opt']['statemodel'] = 'Marked'; 09287 $annots .= ' /StateModel /'.$pl['opt']['statemodel']; 09288 } 09289 if ($pl['opt']['statemodel'] == 'Marked') { 09290 $states = array('Accepted', 'Unmarked'); 09291 } else { 09292 $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None'); 09293 } 09294 if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) { 09295 $annots .= ' /State /'.$pl['opt']['state']; 09296 } else { 09297 if ($pl['opt']['statemodel'] == 'Marked') { 09298 $annots .= ' /State /Unmarked'; 09299 } else { 09300 $annots .= ' /State /None'; 09301 } 09302 } 09303 break; 09304 } 09305 case 'link': { 09306 if (is_string($pl['txt'])) { 09307 // external URI link 09308 $annots .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($pl['txt']), $annot_obj_id).'>>'; 09309 } else { 09310 // internal link 09311 $l = $this->links[$pl['txt']]; 09312 if (isset($this->page_obj_id[($l[0])])) { 09313 $annots .= sprintf(' /Dest [%u 0 R /XYZ 0 %.2F null]', $this->page_obj_id[($l[0])], ($this->pagedim[$l[0]]['h'] - ($l[1] * $this->k))); 09314 } 09315 } 09316 $hmodes = array('N', 'I', 'O', 'P'); 09317 if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) { 09318 $annots .= ' /H /'.$pl['opt']['h']; 09319 } else { 09320 $annots .= ' /H /I'; 09321 } 09322 //$annots .= ' /PA '; 09323 //$annots .= ' /Quadpoints '; 09324 break; 09325 } 09326 case 'freetext': { 09327 if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) { 09328 $annots .= ' /DA ('.$pl['opt']['da'].')'; 09329 } 09330 if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) { 09331 $annots .= ' /Q '.intval($pl['opt']['q']); 09332 } 09333 if (isset($pl['opt']['rc'])) { 09334 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id); 09335 } 09336 if (isset($pl['opt']['ds'])) { 09337 $annots .= ' /DS '.$this->_textstring($pl['opt']['ds'], $annot_obj_id); 09338 } 09339 if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) { 09340 $annots .= ' /CL ['; 09341 foreach ($pl['opt']['cl'] as $cl) { 09342 $annots .= sprintf('%.4F ', $cl * $this->k); 09343 } 09344 $annots .= ']'; 09345 } 09346 $tfit = array('FreeText', 'FreeTextCallout', 'FreeTextTypeWriter'); 09347 if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) { 09348 $annots .= ' /IT /'.$pl['opt']['it']; 09349 } 09350 if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) { 09351 $l = $pl['opt']['rd'][0] * $this->k; 09352 $r = $pl['opt']['rd'][1] * $this->k; 09353 $t = $pl['opt']['rd'][2] * $this->k; 09354 $b = $pl['opt']['rd'][3] * $this->k; 09355 $annots .= ' /RD ['.sprintf('%.2F %.2F %.2F %.2F', $l, $r, $t, $b).']'; 09356 } 09357 if (isset($pl['opt']['le']) AND in_array($pl['opt']['le'], $lineendings)) { 09358 $annots .= ' /LE /'.$pl['opt']['le']; 09359 } 09360 break; 09361 } 09362 case 'line': { 09363 break; 09364 } 09365 case 'square': { 09366 break; 09367 } 09368 case 'circle': { 09369 break; 09370 } 09371 case 'polygon': { 09372 break; 09373 } 09374 case 'polyline': { 09375 break; 09376 } 09377 case 'highlight': { 09378 break; 09379 } 09380 case 'underline': { 09381 break; 09382 } 09383 case 'squiggly': { 09384 break; 09385 } 09386 case 'strikeout': { 09387 break; 09388 } 09389 case 'stamp': { 09390 break; 09391 } 09392 case 'caret': { 09393 break; 09394 } 09395 case 'ink': { 09396 break; 09397 } 09398 case 'popup': { 09399 break; 09400 } 09401 case 'fileattachment': { 09402 if ($this->pdfa_mode) { 09403 // embedded files are not allowed in PDF/A mode 09404 break; 09405 } 09406 if (!isset($pl['opt']['fs'])) { 09407 break; 09408 } 09409 $filename = basename($pl['opt']['fs']); 09410 if (isset($this->embeddedfiles[$filename]['n'])) { 09411 $annots .= ' /FS <</Type /Filespec /F '.$this->_datastring($filename, $annot_obj_id).' /EF <</F '.$this->embeddedfiles[$filename]['n'].' 0 R>> >>'; 09412 $iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag'); 09413 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) { 09414 $annots .= ' /Name /'.$pl['opt']['name']; 09415 } else { 09416 $annots .= ' /Name /PushPin'; 09417 } 09418 } 09419 break; 09420 } 09421 case 'sound': { 09422 if (!isset($pl['opt']['fs'])) { 09423 break; 09424 } 09425 $filename = basename($pl['opt']['fs']); 09426 if (isset($this->embeddedfiles[$filename]['n'])) { 09427 // ... TO BE COMPLETED ... 09428 // /R /C /B /E /CO /CP 09429 $annots .= ' /Sound <</Type /Filespec /F '.$this->_datastring($filename, $annot_obj_id).' /EF <</F '.$this->embeddedfiles[$filename]['n'].' 0 R>> >>'; 09430 $iconsapp = array('Speaker', 'Mic'); 09431 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) { 09432 $annots .= ' /Name /'.$pl['opt']['name']; 09433 } else { 09434 $annots .= ' /Name /Speaker'; 09435 } 09436 } 09437 break; 09438 } 09439 case 'movie': { 09440 break; 09441 } 09442 case 'widget': { 09443 $hmode = array('N', 'I', 'O', 'P', 'T'); 09444 if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmode)) { 09445 $annots .= ' /H /'.$pl['opt']['h']; 09446 } 09447 if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk'])) AND !empty($pl['opt']['mk'])) { 09448 $annots .= ' /MK <<'; 09449 if (isset($pl['opt']['mk']['r'])) { 09450 $annots .= ' /R '.$pl['opt']['mk']['r']; 09451 } 09452 if (isset($pl['opt']['mk']['bc']) AND (is_array($pl['opt']['mk']['bc']))) { 09453 $annots .= ' /BC ['; 09454 foreach($pl['opt']['mk']['bc'] AS $col) { 09455 $col = intval($col); 09456 $color = $col <= 0 ? 0 : ($col >= 255 ? 1 : $col / 255); 09457 $annots .= sprintf(' %.2F', $color); 09458 } 09459 $annots .= ']'; 09460 } 09461 if (isset($pl['opt']['mk']['bg']) AND (is_array($pl['opt']['mk']['bg']))) { 09462 $annots .= ' /BG ['; 09463 foreach($pl['opt']['mk']['bg'] AS $col) { 09464 $col = intval($col); 09465 $color = $col <= 0 ? 0 : ($col >= 255 ? 1 : $col / 255); 09466 $annots .= sprintf(' %.2F', $color); 09467 } 09468 $annots .= ']'; 09469 } 09470 if (isset($pl['opt']['mk']['ca'])) { 09471 $annots .= ' /CA '.$pl['opt']['mk']['ca']; 09472 } 09473 if (isset($pl['opt']['mk']['rc'])) { 09474 $annots .= ' /RC '.$pl['opt']['mk']['rc']; 09475 } 09476 if (isset($pl['opt']['mk']['ac'])) { 09477 $annots .= ' /AC '.$pl['opt']['mk']['ac']; 09478 } 09479 if (isset($pl['opt']['mk']['i'])) { 09480 $info = $this->getImageBuffer($pl['opt']['mk']['i']); 09481 if ($info !== false) { 09482 $annots .= ' /I '.$info['n'].' 0 R'; 09483 } 09484 } 09485 if (isset($pl['opt']['mk']['ri'])) { 09486 $info = $this->getImageBuffer($pl['opt']['mk']['ri']); 09487 if ($info !== false) { 09488 $annots .= ' /RI '.$info['n'].' 0 R'; 09489 } 09490 } 09491 if (isset($pl['opt']['mk']['ix'])) { 09492 $info = $this->getImageBuffer($pl['opt']['mk']['ix']); 09493 if ($info !== false) { 09494 $annots .= ' /IX '.$info['n'].' 0 R'; 09495 } 09496 } 09497 if (isset($pl['opt']['mk']['if']) AND (is_array($pl['opt']['mk']['if'])) AND !empty($pl['opt']['mk']['if'])) { 09498 $annots .= ' /IF <<'; 09499 $if_sw = array('A', 'B', 'S', 'N'); 09500 if (isset($pl['opt']['mk']['if']['sw']) AND in_array($pl['opt']['mk']['if']['sw'], $if_sw)) { 09501 $annots .= ' /SW /'.$pl['opt']['mk']['if']['sw']; 09502 } 09503 $if_s = array('A', 'P'); 09504 if (isset($pl['opt']['mk']['if']['s']) AND in_array($pl['opt']['mk']['if']['s'], $if_s)) { 09505 $annots .= ' /S /'.$pl['opt']['mk']['if']['s']; 09506 } 09507 if (isset($pl['opt']['mk']['if']['a']) AND (is_array($pl['opt']['mk']['if']['a'])) AND !empty($pl['opt']['mk']['if']['a'])) { 09508 $annots .= sprintf(' /A [%.2F %.2F]', $pl['opt']['mk']['if']['a'][0], $pl['opt']['mk']['if']['a'][1]); 09509 } 09510 if (isset($pl['opt']['mk']['if']['fb']) AND ($pl['opt']['mk']['if']['fb'])) { 09511 $annots .= ' /FB true'; 09512 } 09513 $annots .= '>>'; 09514 } 09515 if (isset($pl['opt']['mk']['tp']) AND ($pl['opt']['mk']['tp'] >= 0) AND ($pl['opt']['mk']['tp'] <= 6)) { 09516 $annots .= ' /TP '.intval($pl['opt']['mk']['tp']); 09517 } 09518 $annots .= '>>'; 09519 } // end MK 09520 // --- Entries for field dictionaries --- 09521 if (isset($this->radiobutton_groups[$n][$pl['txt']])) { 09522 // set parent 09523 $annots .= ' /Parent '.$this->radiobutton_groups[$n][$pl['txt']].' 0 R'; 09524 } 09525 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) { 09526 $annots .= ' /T '.$this->_datastring($pl['opt']['t'], $annot_obj_id); 09527 } 09528 if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) { 09529 $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $annot_obj_id); 09530 } 09531 if (isset($pl['opt']['tm']) AND is_string($pl['opt']['tm'])) { 09532 $annots .= ' /TM '.$this->_datastring($pl['opt']['tm'], $annot_obj_id); 09533 } 09534 if (isset($pl['opt']['ff'])) { 09535 if (is_array($pl['opt']['ff'])) { 09536 // array of bit settings 09537 $flag = 0; 09538 foreach($pl['opt']['ff'] as $val) { 09539 $flag += 1 << ($val - 1); 09540 } 09541 } else { 09542 $flag = intval($pl['opt']['ff']); 09543 } 09544 $annots .= ' /Ff '.$flag; 09545 } 09546 if (isset($pl['opt']['maxlen'])) { 09547 $annots .= ' /MaxLen '.intval($pl['opt']['maxlen']); 09548 } 09549 if (isset($pl['opt']['v'])) { 09550 $annots .= ' /V'; 09551 if (is_array($pl['opt']['v'])) { 09552 foreach ($pl['opt']['v'] AS $optval) { 09553 if (is_float($optval)) { 09554 $optval = sprintf('%.2F', $optval); 09555 } 09556 $annots .= ' '.$optval; 09557 } 09558 } else { 09559 $annots .= ' '.$this->_textstring($pl['opt']['v'], $annot_obj_id); 09560 } 09561 } 09562 if (isset($pl['opt']['dv'])) { 09563 $annots .= ' /DV'; 09564 if (is_array($pl['opt']['dv'])) { 09565 foreach ($pl['opt']['dv'] AS $optval) { 09566 if (is_float($optval)) { 09567 $optval = sprintf('%.2F', $optval); 09568 } 09569 $annots .= ' '.$optval; 09570 } 09571 } else { 09572 $annots .= ' '.$this->_textstring($pl['opt']['dv'], $annot_obj_id); 09573 } 09574 } 09575 if (isset($pl['opt']['rv'])) { 09576 $annots .= ' /RV'; 09577 if (is_array($pl['opt']['rv'])) { 09578 foreach ($pl['opt']['rv'] AS $optval) { 09579 if (is_float($optval)) { 09580 $optval = sprintf('%.2F', $optval); 09581 } 09582 $annots .= ' '.$optval; 09583 } 09584 } else { 09585 $annots .= ' '.$this->_textstring($pl['opt']['rv'], $annot_obj_id); 09586 } 09587 } 09588 if (isset($pl['opt']['a']) AND !empty($pl['opt']['a'])) { 09589 $annots .= ' /A << '.$pl['opt']['a'].' >>'; 09590 } 09591 if (isset($pl['opt']['aa']) AND !empty($pl['opt']['aa'])) { 09592 $annots .= ' /AA << '.$pl['opt']['aa'].' >>'; 09593 } 09594 if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) { 09595 $annots .= ' /DA ('.$pl['opt']['da'].')'; 09596 } 09597 if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) { 09598 $annots .= ' /Q '.intval($pl['opt']['q']); 09599 } 09600 if (isset($pl['opt']['opt']) AND (is_array($pl['opt']['opt'])) AND !empty($pl['opt']['opt'])) { 09601 $annots .= ' /Opt ['; 09602 foreach($pl['opt']['opt'] AS $copt) { 09603 if (is_array($copt)) { 09604 $annots .= ' ['.$this->_textstring($copt[0], $annot_obj_id).' '.$this->_textstring($copt[1], $annot_obj_id).']'; 09605 } else { 09606 $annots .= ' '.$this->_textstring($copt, $annot_obj_id); 09607 } 09608 } 09609 $annots .= ']'; 09610 } 09611 if (isset($pl['opt']['ti'])) { 09612 $annots .= ' /TI '.intval($pl['opt']['ti']); 09613 } 09614 if (isset($pl['opt']['i']) AND (is_array($pl['opt']['i'])) AND !empty($pl['opt']['i'])) { 09615 $annots .= ' /I ['; 09616 foreach($pl['opt']['i'] AS $copt) { 09617 $annots .= intval($copt).' '; 09618 } 09619 $annots .= ']'; 09620 } 09621 break; 09622 } 09623 case 'screen': { 09624 break; 09625 } 09626 case 'printermark': { 09627 break; 09628 } 09629 case 'trapnet': { 09630 break; 09631 } 09632 case 'watermark': { 09633 break; 09634 } 09635 case '3d': { 09636 break; 09637 } 09638 default: { 09639 break; 09640 } 09641 } 09642 $annots .= '>>'; 09643 // create new annotation object 09644 $this->_out($this->_getobj($annot_obj_id)."\n".$annots."\n".'endobj'); 09645 if ($formfield AND !isset($this->radiobutton_groups[$n][$pl['txt']])) { 09646 // store reference of form object 09647 $this->form_obj_id[] = $annot_obj_id; 09648 } 09649 } 09650 } 09651 } // end for each page 09652 } 09653 09663 protected function _putAPXObject($w=0, $h=0, $stream='') { 09664 $stream = trim($stream); 09665 $out = $this->_getobj()."\n"; 09666 $this->xobjects['AX'.$this->n] = array('n' => $this->n); 09667 $out .= '<<'; 09668 $out .= ' /Type /XObject'; 09669 $out .= ' /Subtype /Form'; 09670 $out .= ' /FormType 1'; 09671 if ($this->compress) { 09672 $stream = gzcompress($stream); 09673 $out .= ' /Filter /FlateDecode'; 09674 } 09675 $rect = sprintf('%.2F %.2F', $w, $h); 09676 $out .= ' /BBox [0 0 '.$rect.']'; 09677 $out .= ' /Matrix [1 0 0 1 0 0]'; 09678 $out .= ' /Resources 2 0 R'; 09679 $stream = $this->_getrawstream($stream); 09680 $out .= ' /Length '.strlen($stream); 09681 $out .= ' >>'; 09682 $out .= ' stream'."\n".$stream."\n".'endstream'; 09683 $out .= "\n".'endobj'; 09684 $this->_out($out); 09685 return $this->n; 09686 } 09687 09697 protected function _getULONG($str, $offset) { 09698 $v = unpack('Ni', substr($str, $offset, 4)); 09699 return $v['i']; 09700 } 09701 09711 protected function _getUSHORT($str, $offset) { 09712 $v = unpack('ni', substr($str, $offset, 2)); 09713 return $v['i']; 09714 } 09715 09725 protected function _getSHORT($str, $offset) { 09726 $v = unpack('si', substr($str, $offset, 2)); 09727 return $v['i']; 09728 } 09729 09739 protected function _getFWORD($str, $offset) { 09740 $v = $this->_getUSHORT($str, $offset); 09741 if ($v > 0x7fff) { 09742 $v -= 0x10000; 09743 } 09744 return $v; 09745 } 09746 09756 protected function _getUFWORD($str, $offset) { 09757 $v = $this->_getUSHORT($str, $offset); 09758 return $v; 09759 } 09760 09770 protected function _getFIXED($str, $offset) { 09771 // mantissa 09772 $m = $this->_getFWORD($str, $offset); 09773 // fraction 09774 $f = $this->_getUSHORT($str, ($offset + 2)); 09775 $v = floatval(''.$m.'.'.$f.''); 09776 return $v; 09777 } 09778 09788 protected function _getBYTE($str, $offset) { 09789 $v = unpack('Ci', substr($str, $offset, 1)); 09790 return $v['i']; 09791 } 09802 protected function updateCIDtoGIDmap($map, $cid, $gid) { 09803 if (($cid >= 0) AND ($cid <= 0xFFFF) AND ($gid >= 0)) { 09804 if ($gid > 0xFFFF) { 09805 $gid -= 0x10000; 09806 } 09807 $map{($cid * 2)} = chr($gid >> 8); 09808 $map{(($cid * 2) + 1)} = chr($gid & 0xFF); 09809 } 09810 return $map; 09811 } 09812 09825 public function addTTFfont($fontfile, $fonttype='', $enc='', $flags=32, $outpath='') { 09826 if (!file_exists($fontfile)) { 09827 $this->Error('Could not find file: '.$fontfile.''); 09828 } 09829 // font metrics 09830 $fmetric = array(); 09831 // build new font name for TCPDF compatibility 09832 $font_path_parts = pathinfo($fontfile); 09833 if (!isset($font_path_parts['filename'])) { 09834 $font_path_parts['filename'] = substr($font_path_parts['basename'], 0, -(strlen($font_path_parts['extension']) + 1)); 09835 } 09836 $font_name = strtolower($font_path_parts['filename']); 09837 $font_name = preg_replace('/[^a-z0-9_]/', '', $font_name); 09838 $search = array('bold', 'oblique', 'italic', 'regular'); 09839 $replace = array('b', 'i', 'i', ''); 09840 $font_name = str_replace($search, $replace, $font_name); 09841 if (empty($font_name)) { 09842 // set generic name 09843 $font_name = 'tcpdffont'; 09844 } 09845 // set output path 09846 if (empty($outpath)) { 09847 $outpath = $this->_getfontpath(); 09848 } 09849 // check if this font already exist 09850 if (file_exists($outpath.$font_name.'.php')) { 09851 // this font already exist (delete it from fonts folder to rebuild it) 09852 return $font_name; 09853 } 09854 $fmetric['file'] = $font_name.'.z'; 09855 $fmetric['ctg'] = $font_name.'.ctg.z'; 09856 // get font data 09857 $font = file_get_contents($fontfile); 09858 $fmetric['originalsize'] = strlen($font); 09859 // autodetect font type 09860 if (empty($fonttype)) { 09861 if ($this->_getULONG($font, 0) == 0x10000) { 09862 // True Type (Unicode or not) 09863 $fonttype = 'TrueTypeUnicode'; 09864 } elseif (substr($font, 0, 4) == 'OTTO') { 09865 // Open Type (Unicode or not) 09866 $this->Error('Unsupported font format: OpenType with CFF data.'); 09867 } else { 09868 // Type 1 09869 $fonttype = 'Type1'; 09870 } 09871 } 09872 // set font type 09873 switch ($fonttype) { 09874 case 'CID0CT': 09875 case 'CID0CS': 09876 case 'CID0KR': 09877 case 'CID0JP': { 09878 $fmetric['type'] = 'cidfont0'; 09879 break; 09880 } 09881 case 'Type1': { 09882 $fmetric['type'] = 'Type1'; 09883 if (empty($enc) AND (($flags & 4) == 0)) { 09884 $enc = 'cp1252'; 09885 } 09886 break; 09887 } 09888 case 'TrueType': { 09889 $fmetric['type'] = 'TrueType'; 09890 break; 09891 } 09892 case 'TrueTypeUnicode': 09893 default: { 09894 $fmetric['type'] = 'TrueTypeUnicode'; 09895 break; 09896 } 09897 } 09898 // set encoding maps (if any) 09899 $fmetric['enc'] = preg_replace('/[^A-Za-z0-9_\-]/', '', $enc); 09900 $fmetric['diff'] = ''; 09901 if (($fmetric['type'] == 'TrueType') OR ($fmetric['type'] == 'Type1')) { 09902 if (!empty($enc) AND ($enc != 'cp1252') AND isset($this->encmaps->encmap[$enc])) { 09903 // build differences from reference encoding 09904 $enc_ref = $this->encmaps->encmap['cp1252']; 09905 $enc_target = $this->encmaps->encmap[$enc]; 09906 $last = 0; 09907 for ($i = 32; $i <= 255; ++$i) { 09908 if ($enc_target != $enc_ref[$i]) { 09909 if ($i != ($last + 1)) { 09910 $fmetric['diff'] .= $i.' '; 09911 } 09912 $last = $i; 09913 $fmetric['diff'] .= '/'.$enc_target[$i].' '; 09914 } 09915 } 09916 } 09917 } 09918 // parse the font by type 09919 if ($fmetric['type'] == 'Type1') { 09920 // ---------- TYPE 1 ---------- 09921 // read first segment 09922 $a = unpack('Cmarker/Ctype/Vsize', substr($font, 0, 6)); 09923 if ($a['marker'] != 128) { 09924 $this->Error('Font file is not a valid binary Type1'); 09925 } 09926 $fmetric['size1'] = $a['size']; 09927 $data = substr($font, 6, $fmetric['size1']); 09928 // read second segment 09929 $a = unpack('Cmarker/Ctype/Vsize', substr($font, (6 + $fmetric['size1']), 6)); 09930 if ($a['marker'] != 128) { 09931 $this->Error('Font file is not a valid binary Type1'); 09932 } 09933 $fmetric['size2'] = $a['size']; 09934 $encrypted = substr($font, (12 + $fmetric['size1']), $fmetric['size2']); 09935 $data .= $encrypted; 09936 // store compressed font 09937 $fp = fopen($outpath.$fmetric['file'], 'wb'); 09938 fwrite($fp, gzcompress($data)); 09939 fclose($fp); 09940 // get font info 09941 $fmetric['Flags'] = $flags; 09942 preg_match ('#/FullName[\s]*\(([^\)]*)#', $font, $matches); 09943 $fmetric['name'] = preg_replace('/[^a-zA-Z0-9_\-]/', '', $matches[1]); 09944 preg_match('#/FontBBox[\s]*{([^}]*)#', $font, $matches); 09945 $fmetric['bbox'] = trim($matches[1]); 09946 $bv = explode(' ', $fmetric['bbox']); 09947 $fmetric['Ascent'] = intval($bv[3]); 09948 $fmetric['Descent'] = intval($bv[1]); 09949 preg_match('#/ItalicAngle[\s]*([0-9\+\-]*)#', $font, $matches); 09950 $fmetric['italicAngle'] = intval($matches[1]); 09951 if ($fmetric['italicAngle'] != 0) { 09952 $fmetric['Flags'] |= 64; 09953 } 09954 preg_match('#/UnderlinePosition[\s]*([0-9\+\-]*)#', $font, $matches); 09955 $fmetric['underlinePosition'] = intval($matches[1]); 09956 preg_match('#/UnderlineThickness[\s]*([0-9\+\-]*)#', $font, $matches); 09957 $fmetric['underlineThickness'] = intval($matches[1]); 09958 preg_match('#/isFixedPitch[\s]*([^\s]*)#', $font, $matches); 09959 if ($matches[1] == 'true') { 09960 $fmetric['Flags'] |= 1; 09961 } 09962 // get internal map 09963 $imap = array(); 09964 if (preg_match_all('#dup[\s]([0-9]+)[\s]*/([^\s]*)[\s]put#sU', $font, $fmap, PREG_SET_ORDER) > 0) { 09965 foreach ($fmap as $v) { 09966 $imap[$v[2]] = $v[1]; 09967 } 09968 } 09969 // decrypt eexec encrypted part 09970 $r = 55665; // eexec encryption constant 09971 $c1 = 52845; 09972 $c2 = 22719; 09973 $elen = strlen($encrypted); 09974 $eplain = ''; 09975 for ($i = 0; $i < $elen; ++$i) { 09976 $chr = ord($encrypted{$i}); 09977 $eplain .= chr($chr ^ ($r >> 8)); 09978 $r = ((($chr + $r) * $c1 + $c2) % 65536); 09979 } 09980 if (preg_match('#/ForceBold[\s]*([^\s]*)#', $eplain, $matches) > 0) { 09981 if ($matches[1] == 'true') { 09982 $fmetric['Flags'] |= 0x40000; 09983 } 09984 } 09985 if (preg_match('#/StdVW[\s]*\[([^\]]*)#', $eplain, $matches) > 0) { 09986 $fmetric['StemV'] = intval($matches[1]); 09987 } else { 09988 $fmetric['StemV'] = 70; 09989 } 09990 if (preg_match('#/StdHW[\s]*\[([^\]]*)#', $eplain, $matches) > 0) { 09991 $fmetric['StemH'] = intval($matches[1]); 09992 } else { 09993 $fmetric['StemH'] = 30; 09994 } 09995 if (preg_match('#/BlueValues[\s]*\[([^\]]*)#', $eplain, $matches) > 0) { 09996 $bv = explode(' ', $matches[1]); 09997 if (count($bv) >= 6) { 09998 $v1 = intval($bv[2]); 09999 $v2 = intval($bv[4]); 10000 if ($v1 <= $v2) { 10001 $fmetric['XHeight'] = $v1; 10002 $fmetric['CapHeight'] = $v2; 10003 } else { 10004 $fmetric['XHeight'] = $v2; 10005 $fmetric['CapHeight'] = $v1; 10006 } 10007 } else { 10008 $fmetric['XHeight'] = 450; 10009 $fmetric['CapHeight'] = 700; 10010 } 10011 } else { 10012 $fmetric['XHeight'] = 450; 10013 $fmetric['CapHeight'] = 700; 10014 } 10015 // get the number of random bytes at the beginning of charstrings 10016 if (preg_match('#/lenIV[\s]*([0-9]*)#', $eplain, $matches) > 0) { 10017 $lenIV = intval($matches[1]); 10018 } else { 10019 $lenIV = 4; 10020 } 10021 $fmetric['Leading'] = 0; 10022 // get charstring data 10023 $eplain = substr($eplain, (strpos($eplain, '/CharStrings') + 1)); 10024 preg_match_all('#/([A-Za-z0-9\.]*)[\s][0-9]+[\s]RD[\s](.*)[\s]ND#sU', $eplain, $matches, PREG_SET_ORDER); 10025 if (!empty($enc) AND isset($this->encmaps->encmap[$enc])) { 10026 $enc_map = $this->encmaps->encmap[$enc]; 10027 } else { 10028 $enc_map = false; 10029 } 10030 $fmetric['cw'] = ''; 10031 $fmetric['MaxWidth'] = 0; 10032 $cwidths = array(); 10033 foreach ($matches as $k => $v) { 10034 $cid = 0; 10035 if (isset($imap[$v[1]])) { 10036 $cid = $imap[$v[1]]; 10037 } elseif ($enc_map !== false) { 10038 $cid = array_search($v[1], $enc_map); 10039 if ($cid === false) { 10040 $cid = 0; 10041 } elseif ($cid > 1000) { 10042 $cid -= 1000; 10043 } 10044 } 10045 // decrypt charstring encrypted part 10046 $r = 4330; // charstring encryption constant 10047 $c1 = 52845; 10048 $c2 = 22719; 10049 $cd = $v[2]; 10050 $clen = strlen($cd); 10051 $ccom = array(); 10052 for ($i = 0; $i < $clen; ++$i) { 10053 $chr = ord($cd{$i}); 10054 $ccom[] = ($chr ^ ($r >> 8)); 10055 $r = ((($chr + $r) * $c1 + $c2) % 65536); 10056 } 10057 // decode numbers 10058 $cdec = array(); 10059 $ck = 0; 10060 $i = $lenIV; 10061 while ($i < $clen) { 10062 if ($ccom[$i] < 32) { 10063 $cdec[$ck] = $ccom[$i]; 10064 if (($ck > 0) AND ($cdec[$ck] == 13)) { 10065 // hsbw command: update width 10066 $cwidths[$cid] = $cdec[($ck - 1)]; 10067 } 10068 ++$i; 10069 } elseif (($ccom[$i] >= 32) AND ($ccom[$i] <= 246)) { 10070 $cdec[$ck] = ($ccom[$i] - 139); 10071 ++$i; 10072 } elseif (($ccom[$i] >= 247) AND ($ccom[$i] <= 250)) { 10073 $cdec[$ck] = ((($ccom[$i] - 247) * 256) + $ccom[($i + 1)] + 108); 10074 $i += 2; 10075 } elseif (($ccom[$i] >= 251) AND ($ccom[$i] <= 254)) { 10076 $cdec[$ck] = ((-($ccom[$i] - 251) * 256) - $ccom[($i + 1)] - 108); 10077 $i += 2; 10078 } elseif ($ccom[$i] == 255) { 10079 $sval = chr($ccom[($i + 1)]).chr($ccom[($i + 2)]).chr($ccom[($i + 3)]).chr($ccom[($i + 4)]); 10080 $vsval = unpack('li', $sval); 10081 $cdec[$ck] = $vsval['i']; 10082 $i += 5; 10083 } 10084 ++$ck; 10085 } 10086 } // end for each matches 10087 $fmetric['MissingWidth'] = $cwidths[0]; 10088 $fmetric['MaxWidth'] = $fmetric['MissingWidth']; 10089 $fmetric['AvgWidth'] = 0; 10090 // set chars widths 10091 for ($cid = 0; $cid <= 255; ++$cid) { 10092 if (isset($cwidths[$cid])) { 10093 if ($cwidths[$cid] > $fmetric['MaxWidth']) { 10094 $fmetric['MaxWidth'] = $cwidths[$cid]; 10095 } 10096 $fmetric['AvgWidth'] += $cwidths[$cid]; 10097 $fmetric['cw'] .= ','.$cid.'=>'.$cwidths[$cid]; 10098 } else { 10099 $fmetric['cw'] .= ','.$cid.'=>'.$fmetric['MissingWidth']; 10100 } 10101 } 10102 $fmetric['AvgWidth'] = round($fmetric['AvgWidth'] / count($cwidths)); 10103 } else { 10104 // ---------- TRUE TYPE ---------- 10105 if ($fmetric['type'] != 'cidfont0') { 10106 // store compressed font 10107 $fp = fopen($outpath.$fmetric['file'], 'wb'); 10108 fwrite($fp, gzcompress($font)); 10109 fclose($fp); 10110 } 10111 $offset = 0; // offset position of the font data 10112 if ($this->_getULONG($font, $offset) != 0x10000) { 10113 // sfnt version must be 0x00010000 for TrueType version 1.0. 10114 return $font; 10115 } 10116 $offset += 4; 10117 // get number of tables 10118 $numTables = $this->_getUSHORT($font, $offset); 10119 $offset += 2; 10120 // skip searchRange, entrySelector and rangeShift 10121 $offset += 6; 10122 // tables array 10123 $table = array(); 10124 // ---------- get tables ---------- 10125 for ($i = 0; $i < $numTables; ++$i) { 10126 // get table info 10127 $tag = substr($font, $offset, 4); 10128 $offset += 4; 10129 $table[$tag] = array(); 10130 $table[$tag]['checkSum'] = $this->_getULONG($font, $offset); 10131 $offset += 4; 10132 $table[$tag]['offset'] = $this->_getULONG($font, $offset); 10133 $offset += 4; 10134 $table[$tag]['length'] = $this->_getULONG($font, $offset); 10135 $offset += 4; 10136 } 10137 // check magicNumber 10138 $offset = $table['head']['offset'] + 12; 10139 if ($this->_getULONG($font, $offset) != 0x5F0F3CF5) { 10140 // magicNumber must be 0x5F0F3CF5 10141 return $font; 10142 } 10143 $offset += 4; 10144 $offset += 2; // skip flags 10145 // get FUnits 10146 $fmetric['unitsPerEm'] = $this->_getUSHORT($font, $offset); 10147 $offset += 2; 10148 // units ratio constant 10149 $urk = (1000 / $fmetric['unitsPerEm']); 10150 $offset += 16; // skip created, modified 10151 $xMin = round($this->_getFWORD($font, $offset) * $urk); 10152 $offset += 2; 10153 $yMin = round($this->_getFWORD($font, $offset) * $urk); 10154 $offset += 2; 10155 $xMax = round($this->_getFWORD($font, $offset) * $urk); 10156 $offset += 2; 10157 $yMax = round($this->_getFWORD($font, $offset) * $urk); 10158 $offset += 2; 10159 $fmetric['bbox'] = ''.$xMin.' '.$yMin.' '.$xMax.' '.$yMax.''; 10160 $macStyle = $this->_getUSHORT($font, $offset); 10161 $offset += 2; 10162 // PDF font flags 10163 $fmetric['Flags'] = $flags; 10164 if (($macStyle & 2) == 2) { 10165 // italic flag 10166 $fmetric['Flags'] |= 64; 10167 } 10168 // get offset mode (indexToLocFormat : 0 = short, 1 = long) 10169 $offset = $table['head']['offset'] + 50; 10170 $short_offset = ($this->_getSHORT($font, $offset) == 0); 10171 $offset += 2; 10172 // get the offsets to the locations of the glyphs in the font, relative to the beginning of the glyphData table 10173 $indexToLoc = array(); 10174 $offset = $table['loca']['offset']; 10175 if ($short_offset) { 10176 // short version 10177 $tot_num_glyphs = ($table['loca']['length'] / 2); // numGlyphs + 1 10178 for ($i = 0; $i < $tot_num_glyphs; ++$i) { 10179 $indexToLoc[$i] = $this->_getUSHORT($font, $offset) * 2; 10180 $offset += 2; 10181 } 10182 } else { 10183 // long version 10184 $tot_num_glyphs = ($table['loca']['length'] / 4); // numGlyphs + 1 10185 for ($i = 0; $i < $tot_num_glyphs; ++$i) { 10186 $indexToLoc[$i] = $this->_getULONG($font, $offset); 10187 $offset += 4; 10188 } 10189 } 10190 // get glyphs indexes of chars from cmap table 10191 $offset = $table['cmap']['offset'] + 2; 10192 $numEncodingTables = $this->_getUSHORT($font, $offset); 10193 $offset += 2; 10194 $encodingTables = array(); 10195 for ($i = 0; $i < $numEncodingTables; ++$i) { 10196 $encodingTables[$i]['platformID'] = $this->_getUSHORT($font, $offset); 10197 $offset += 2; 10198 $encodingTables[$i]['encodingID'] = $this->_getUSHORT($font, $offset); 10199 $offset += 2; 10200 $encodingTables[$i]['offset'] = $this->_getULONG($font, $offset); 10201 $offset += 4; 10202 } 10203 // ---------- get os/2 metrics ---------- 10204 $offset = $table['OS/2']['offset']; 10205 $offset += 2; // skip version 10206 // xAvgCharWidth 10207 $fmetric['AvgWidth'] = round($this->_getFWORD($font, $offset) * $urk); 10208 $offset += 2; 10209 // usWeightClass 10210 $usWeightClass = round($this->_getUFWORD($font, $offset) * $urk); 10211 // estimate StemV and StemH (400 = usWeightClass for Normal - Regular font) 10212 $fmetric['StemV'] = round((70 * $usWeightClass) / 400); 10213 $fmetric['StemH'] = round((30 * $usWeightClass) / 400); 10214 $offset += 2; 10215 $offset += 2; // usWidthClass 10216 $fsType = $this->_getSHORT($font, $offset); 10217 $offset += 2; 10218 if ($fsType == 2) { 10219 $this->Error('This Font cannot be modified, embedded or exchanged in any manner without first obtaining permission of the legal owner.'); 10220 } 10221 // ---------- get font name ---------- 10222 $fmetric['name'] = ''; 10223 $offset = $table['name']['offset']; 10224 $offset += 2; // skip Format selector (=0). 10225 // Number of NameRecords that follow n. 10226 $numNameRecords = $this->_getUSHORT($font, $offset); 10227 $offset += 2; 10228 // Offset to start of string storage (from start of table). 10229 $stringStorageOffset = $this->_getUSHORT($font, $offset); 10230 $offset += 2; 10231 for ($i = 0; $i < $numNameRecords; ++$i) { 10232 $offset += 6; // skip Platform ID, Platform-specific encoding ID, Language ID. 10233 // Name ID. 10234 $nameID = $this->_getUSHORT($font, $offset); 10235 $offset += 2; 10236 if ($nameID == 6) { 10237 // String length (in bytes). 10238 $stringLength = $this->_getUSHORT($font, $offset); 10239 $offset += 2; 10240 // String offset from start of storage area (in bytes). 10241 $stringOffset = $this->_getUSHORT($font, $offset); 10242 $offset += 2; 10243 $offset = ($table['name']['offset'] + $stringStorageOffset + $stringOffset); 10244 $fmetric['name'] = substr($font, $offset, $stringLength); 10245 $fmetric['name'] = preg_replace('/[^a-zA-Z0-9_\-]/', '', $fmetric['name']); 10246 break; 10247 } else { 10248 $offset += 4; // skip String length, String offset 10249 } 10250 } 10251 if (empty($fmetric['name'])) { 10252 $fmetric['name'] = $font_name; 10253 } 10254 // ---------- get post data ---------- 10255 $offset = $table['post']['offset']; 10256 $offset += 4; // skip Format Type 10257 $fmetric['italicAngle'] = $this->_getFIXED($font, $offset); 10258 $offset += 4; 10259 $fmetric['underlinePosition'] = round($this->_getFWORD($font, $offset) * $urk); 10260 $offset += 2; 10261 $fmetric['underlineThickness'] = round($this->_getFWORD($font, $offset) * $urk); 10262 $offset += 2; 10263 $isFixedPitch = ($this->_getULONG($font, $offset) == 0) ? false : true; 10264 $offset += 2; 10265 if ($isFixedPitch) { 10266 $fmetric['Flags'] |= 1; 10267 } 10268 // ---------- get hhea data ---------- 10269 $offset = $table['hhea']['offset']; 10270 $offset += 4; // skip Table version number 10271 // Ascender 10272 $fmetric['Ascent'] = round($this->_getFWORD($font, $offset) * $urk); 10273 $offset += 2; 10274 // Descender 10275 $fmetric['Descent'] = round($this->_getFWORD($font, $offset) * $urk); 10276 $offset += 2; 10277 // LineGap 10278 $fmetric['Leading'] = round($this->_getFWORD($font, $offset) * $urk); 10279 $offset += 2; 10280 // advanceWidthMax 10281 $fmetric['MaxWidth'] = round($this->_getUFWORD($font, $offset) * $urk); 10282 $offset += 2; 10283 $offset += 22; // skip some values 10284 // get the number of hMetric entries in hmtx table 10285 $numberOfHMetrics = $this->_getUSHORT($font, $offset); 10286 // ---------- get maxp data ---------- 10287 $offset = $table['maxp']['offset']; 10288 $offset += 4; // skip Table version number 10289 // get the the number of glyphs in the font. 10290 $numGlyphs = $this->_getUSHORT($font, $offset); 10291 // ---------- get CIDToGIDMap ---------- 10292 $ctg = array(); 10293 foreach ($encodingTables as $enctable) { 10294 if (($enctable['platformID'] == 3) AND ($enctable['encodingID'] == 0)) { 10295 $modesymbol = true; 10296 } else { 10297 $modesymbol = false; 10298 } 10299 $offset = $table['cmap']['offset'] + $enctable['offset']; 10300 $format = $this->_getUSHORT($font, $offset); 10301 $offset += 2; 10302 switch ($format) { 10303 case 0: { // Format 0: Byte encoding table 10304 $offset += 4; // skip length and version/language 10305 for ($c = 0; $c < 256; ++$c) { 10306 $g = $this->_getBYTE($font, $offset); 10307 $ctg[$c] = $g; 10308 ++$offset; 10309 } 10310 break; 10311 } 10312 case 2: { // Format 2: High-byte mapping through table 10313 $offset += 4; // skip length and version/language 10314 $numSubHeaders = 0; 10315 for ($i = 0; $i < 256; ++$i) { 10316 // Array that maps high bytes to subHeaders: value is subHeader index * 8. 10317 $subHeaderKeys[$i] = ($this->_getUSHORT($font, $offset) / 8); 10318 $offset += 2; 10319 if ($numSubHeaders < $subHeaderKeys[$i]) { 10320 $numSubHeaders = $subHeaderKeys[$i]; 10321 } 10322 } 10323 // the number of subHeaders is equal to the max of subHeaderKeys + 1 10324 ++$numSubHeaders; 10325 // read subHeader structures 10326 $subHeaders = array(); 10327 $numGlyphIndexArray = 0; 10328 for ($k = 0; $k < $numSubHeaders; ++$k) { 10329 $subHeaders[$k]['firstCode'] = $this->_getUSHORT($font, $offset); 10330 $offset += 2; 10331 $subHeaders[$k]['entryCount'] = $this->_getUSHORT($font, $offset); 10332 $offset += 2; 10333 $subHeaders[$k]['idDelta'] = $this->_getSHORT($font, $offset); 10334 $offset += 2; 10335 $subHeaders[$k]['idRangeOffset'] = $this->_getUSHORT($font, $offset); 10336 $offset += 2; 10337 $subHeaders[$k]['idRangeOffset'] -= (2 + (($numSubHeaders - $k - 1) * 8)); 10338 $subHeaders[$k]['idRangeOffset'] /= 2; 10339 $numGlyphIndexArray += $subHeaders[$k]['entryCount']; 10340 } 10341 for ($k = 0; $k < $numGlyphIndexArray; ++$k) { 10342 $glyphIndexArray[$k] = $this->_getUSHORT($font, $offset); 10343 $offset += 2; 10344 } 10345 for ($i = 0; $i < 256; ++$i) { 10346 $k = $subHeaderKeys[$i]; 10347 if ($k == 0) { 10348 // one byte code 10349 $c = $i; 10350 $g = $glyphIndexArray[0]; 10351 $ctg[$c] = $g; 10352 } else { 10353 // two bytes code 10354 $start_byte = $subHeaders[$k]['firstCode']; 10355 $end_byte = $start_byte + $subHeaders[$k]['entryCount']; 10356 for ($j = $start_byte; $j < $end_byte; ++$j) { 10357 // combine high and low bytes 10358 $c = (($i << 8) + $j); 10359 $idRangeOffset = ($subHeaders[$k]['idRangeOffset'] + $j - $subHeaders[$k]['firstCode']); 10360 $g = $glyphIndexArray[$idRangeOffset]; 10361 $g += ($idDelta[$k] - 65536); 10362 if ($g < 0) { 10363 $g = 0; 10364 } 10365 $ctg[$c] = $g; 10366 } 10367 } 10368 } 10369 break; 10370 } 10371 case 4: { // Format 4: Segment mapping to delta values 10372 $length = $this->_getUSHORT($font, $offset); 10373 $offset += 2; 10374 $offset += 2; // skip version/language 10375 $segCount = ($this->_getUSHORT($font, $offset) / 2); 10376 $offset += 2; 10377 $offset += 6; // skip searchRange, entrySelector, rangeShift 10378 $endCount = array(); // array of end character codes for each segment 10379 for ($k = 0; $k < $segCount; ++$k) { 10380 $endCount[$k] = $this->_getUSHORT($font, $offset); 10381 $offset += 2; 10382 } 10383 $offset += 2; // skip reservedPad 10384 $startCount = array(); // array of start character codes for each segment 10385 for ($k = 0; $k < $segCount; ++$k) { 10386 $startCount[$k] = $this->_getUSHORT($font, $offset); 10387 $offset += 2; 10388 } 10389 $idDelta = array(); // delta for all character codes in segment 10390 for ($k = 0; $k < $segCount; ++$k) { 10391 $idDelta[$k] = $this->_getUSHORT($font, $offset); 10392 $offset += 2; 10393 } 10394 $idRangeOffset = array(); // Offsets into glyphIdArray or 0 10395 for ($k = 0; $k < $segCount; ++$k) { 10396 $idRangeOffset[$k] = $this->_getUSHORT($font, $offset); 10397 $offset += 2; 10398 } 10399 $gidlen = ($length / 2) - 8 - (4 * $segCount); 10400 $glyphIdArray = array(); // glyph index array 10401 for ($k = 0; $k < $gidlen; ++$k) { 10402 $glyphIdArray[$k] = $this->_getUSHORT($font, $offset); 10403 $offset += 2; 10404 } 10405 for ($k = 0; $k < $segCount; ++$k) { 10406 for ($c = $startCount[$k]; $c <= $endCount[$k]; ++$c) { 10407 if ($idRangeOffset[$k] == 0) { 10408 $g = $c; 10409 } else { 10410 $gid = (($idRangeOffset[$k] / 2) + ($c - $startCount[$k]) - ($segCount - $k)); 10411 $g = $glyphIdArray[$gid]; 10412 } 10413 $g += ($idDelta[$k] - 65536); 10414 if ($g < 0) { 10415 $g = 0; 10416 } 10417 $ctg[$c] = $g; 10418 } 10419 } 10420 break; 10421 } 10422 case 6: { // Format 6: Trimmed table mapping 10423 $offset += 4; // skip length and version/language 10424 $firstCode = $this->_getUSHORT($font, $offset); 10425 $offset += 2; 10426 $entryCount = $this->_getUSHORT($font, $offset); 10427 $offset += 2; 10428 for ($k = 0; $k < $entryCount; ++$k) { 10429 $c = ($k + $firstCode); 10430 $g = $this->_getUSHORT($font, $offset); 10431 $ctg[$c] = $g; 10432 $offset += 2; 10433 } 10434 break; 10435 } 10436 case 8: { // Format 8: Mixed 16-bit and 32-bit coverage 10437 $offset += 10; // skip reserved, length and version/language 10438 for ($k = 0; $k < 8192; ++$k) { 10439 $is32[$k] = $this->_getBYTE($font, $offset); 10440 ++$offset; 10441 } 10442 $nGroups = $this->_getULONG($font, $offset); 10443 $offset += 4; 10444 for ($i = 0; $i < $nGroups; ++$i) { 10445 $startCharCode = $this->_getULONG($font, $offset); 10446 $offset += 4; 10447 $endCharCode = $this->_getULONG($font, $offset); 10448 $offset += 4; 10449 $startGlyphID = $this->_getULONG($font, $offset); 10450 $offset += 4; 10451 for ($k = $startCharCode; $k <= $endCharCode; ++$k) { 10452 $is32idx = floor($c / 8); 10453 if ((isset($is32[$is32idx])) AND (($is32[$is32idx] & (1 << (7 - ($c % 8)))) == 0)) { 10454 $c = $k; 10455 } else { 10456 // 32 bit format 10457 // convert to decimal (http://www.unicode.org/faq//utf_bom.html#utf16-4) 10458 //LEAD_OFFSET = (0xD800 - (0x10000 >> 10)) = 55232 10459 //SURROGATE_OFFSET = (0x10000 - (0xD800 << 10) - 0xDC00) = -56613888 10460 $c = ((55232 + ($k >> 10)) << 10) + (0xDC00 + ($k & 0x3FF)) -56613888; 10461 } 10462 $ctg[$c] = $g; 10463 ++$startGlyphID; 10464 } 10465 } 10466 break; 10467 } 10468 case 10: { // Format 10: Trimmed array 10469 $offset += 10; // skip reserved, length and version/language 10470 $startCharCode = $this->_getULONG($font, $offset); 10471 $offset += 4; 10472 $numChars = $this->_getULONG($font, $offset); 10473 $offset += 4; 10474 for ($k = 0; $k < $numChars; ++$k) { 10475 $c = ($k + $startCharCode); 10476 $g = $this->_getUSHORT($font, $offset); 10477 $ctg[$c] = $g; 10478 $offset += 2; 10479 } 10480 break; 10481 } 10482 case 12: { // Format 12: Segmented coverage 10483 $offset += 10; // skip length and version/language 10484 $nGroups = $this->_getULONG($font, $offset); 10485 $offset += 4; 10486 for ($k = 0; $k < $nGroups; ++$k) { 10487 $startCharCode = $this->_getULONG($font, $offset); 10488 $offset += 4; 10489 $endCharCode = $this->_getULONG($font, $offset); 10490 $offset += 4; 10491 $startGlyphCode = $this->_getULONG($font, $offset); 10492 $offset += 4; 10493 for ($c = $startCharCode; $c <= $endCharCode; ++$c) { 10494 $ctg[$c] = $startGlyphCode; 10495 ++$startGlyphCode; 10496 } 10497 } 10498 break; 10499 } 10500 case 13: { // Format 13: Many-to-one range mappings 10501 // to be implemented ... 10502 break; 10503 } 10504 case 14: { // Format 14: Unicode Variation Sequences 10505 // to be implemented ... 10506 break; 10507 } 10508 } 10509 } 10510 if (!isset($ctg[0])) { 10511 $ctg[0] = 0; 10512 } 10513 // get xHeight (height of x) 10514 $offset = ($table['glyf']['offset'] + $indexToLoc[$ctg[120]] + 4); 10515 $yMin = $this->_getFWORD($font, $offset); 10516 $offset += 4; 10517 $yMax = $this->_getFWORD($font, $offset); 10518 $offset += 2; 10519 $fmetric['XHeight'] = round(($yMax - $yMin) * $urk); 10520 // get CapHeight (height of H) 10521 $offset = ($table['glyf']['offset'] + $indexToLoc[$ctg[72]] + 4); 10522 $yMin = $this->_getFWORD($font, $offset); 10523 $offset += 4; 10524 $yMax = $this->_getFWORD($font, $offset); 10525 $offset += 2; 10526 $fmetric['CapHeight'] = round(($yMax - $yMin) * $urk); 10527 // ceate widths array 10528 $cw = array(); 10529 $offset = $table['hmtx']['offset']; 10530 for ($i = 0 ; $i < $numberOfHMetrics; ++$i) { 10531 $cw[$i] = round($this->_getUFWORD($font, $offset) * $urk); 10532 $offset += 4; // skip lsb 10533 } 10534 if ($numberOfHMetrics < $numGlyphs) { 10535 // fill missing widths with the last value 10536 $cw = array_pad($cw, $numGlyphs, $cw[($numberOfHMetrics - 1)]); 10537 } 10538 $fmetric['MissingWidth'] = $cw[0]; 10539 $fmetric['cw'] = ''; 10540 for ($cid = 0; $cid <= 65535; ++$cid) { 10541 if (isset($ctg[$cid]) AND isset($cw[$ctg[$cid]])) { 10542 $fmetric['cw'] .= ','.$cid.'=>'.$cw[$ctg[$cid]]; 10543 } 10544 } 10545 } // end of true type 10546 if (($fmetric['type'] == 'TrueTypeUnicode') AND (count($ctg) == 256)) { 10547 $fmetric['type'] == 'TrueType'; 10548 } 10549 // ---------- create php font file ---------- 10550 $pfile = '<'.'?'.'php'."\n"; 10551 $pfile .= '// TCPDF FONT FILE DESCRIPTION'."\n"; 10552 $pfile .= '$type=\''.$fmetric['type'].'\';'."\n"; 10553 $pfile .= '$name=\''.$fmetric['name'].'\';'."\n"; 10554 $pfile .= '$up='.$fmetric['underlinePosition'].';'."\n"; 10555 $pfile .= '$ut='.$fmetric['underlineThickness'].';'."\n"; 10556 if ($fmetric['MissingWidth'] > 0) { 10557 $pfile .= '$dw='.$fmetric['MissingWidth'].';'."\n"; 10558 } else { 10559 $pfile .= '$dw='.$fmetric['AvgWidth'].';'."\n"; 10560 } 10561 $pfile .= '$diff=\''.$fmetric['diff'].'\';'."\n"; 10562 if ($fmetric['type'] == 'Type1') { 10563 // Type 1 10564 $pfile .= '$enc=\''.$fmetric['enc'].'\';'."\n"; 10565 $pfile .= '$file=\''.$fmetric['file'].'\';'."\n"; 10566 $pfile .= '$size1='.$fmetric['size1'].';'."\n"; 10567 $pfile .= '$size2='.$fmetric['size2'].';'."\n"; 10568 } else { 10569 $pfile .= '$originalsize='.$fmetric['originalsize'].';'."\n"; 10570 if ($fmetric['type'] == 'cidfont0') { 10571 // CID-0 10572 switch ($fonttype) { 10573 case 'CID0JP': { 10574 $pfile .= '// Japanese'."\n"; 10575 $pfile .= '$enc=\'UniJIS-UTF16-H\';'."\n"; 10576 $pfile .= '$cidinfo=array(\'Registry\'=>\'Adobe\', \'Ordering\'=>\'Japan1\',\'Supplement\'=>5);'."\n"; 10577 $pfile .= 'include(dirname(__FILE__).\'/uni2cid_aj16.php\');'."\n"; 10578 break; 10579 } 10580 case 'CID0KR': { 10581 $pfile .= '// Korean'."\n"; 10582 $pfile .= '$enc=\'UniKS-UTF16-H\';'."\n"; 10583 $pfile .= '$cidinfo=array(\'Registry\'=>\'Adobe\', \'Ordering\'=>\'Korea1\',\'Supplement\'=>0);'."\n"; 10584 $pfile .= 'include(dirname(__FILE__).\'/uni2cid_ak12.php\');'."\n"; 10585 break; 10586 } 10587 case 'CID0CS': { 10588 $pfile .= '// Chinese Simplified'."\n"; 10589 $pfile .= '$enc=\'UniGB-UTF16-H\';'."\n"; 10590 $pfile .= '$cidinfo=array(\'Registry\'=>\'Adobe\', \'Ordering\'=>\'GB1\',\'Supplement\'=>2);'."\n"; 10591 $pfile .= 'include(dirname(__FILE__).\'/uni2cid_ag15.php\');'."\n"; 10592 break; 10593 } 10594 case 'CID0CT': 10595 default: { 10596 $pfile .= '// Chinese Traditional'."\n"; 10597 $pfile .= '$enc=\'UniCNS-UTF16-H\';'."\n"; 10598 $pfile .= '$cidinfo=array(\'Registry\'=>\'Adobe\', \'Ordering\'=>\'CNS1\',\'Supplement\'=>0);'."\n"; 10599 $pfile .= 'include(dirname(__FILE__).\'/uni2cid_aj16.php\');'."\n"; 10600 break; 10601 } 10602 } 10603 } else { 10604 // TrueType 10605 $pfile .= '$enc=\''.$fmetric['enc'].'\';'."\n"; 10606 $pfile .= '$file=\''.$fmetric['file'].'\';'."\n"; 10607 $pfile .= '$ctg=\''.$fmetric['ctg'].'\';'."\n"; 10608 // create CIDToGIDMap 10609 $cidtogidmap = str_pad('', 131072, "\x00"); // (256 * 256 * 2) = 131072 10610 foreach ($ctg as $cid => $gid) { 10611 $cidtogidmap = $this->updateCIDtoGIDmap($cidtogidmap, $cid, $ctg[$cid]); 10612 } 10613 // store compressed CIDToGIDMap 10614 $fp = fopen($outpath.$fmetric['ctg'], 'wb'); 10615 fwrite($fp, gzcompress($cidtogidmap)); 10616 fclose($fp); 10617 } 10618 } 10619 $pfile .= '$desc=array('; 10620 $pfile .= '\'Flags\'=>'.$fmetric['Flags'].','; 10621 $pfile .= '\'FontBBox\'=>\'['.$fmetric['bbox'].']\','; 10622 $pfile .= '\'ItalicAngle\'=>'.$fmetric['italicAngle'].','; 10623 $pfile .= '\'Ascent\'=>'.$fmetric['Ascent'].','; 10624 $pfile .= '\'Descent\'=>'.$fmetric['Descent'].','; 10625 $pfile .= '\'Leading\'=>'.$fmetric['Leading'].','; 10626 $pfile .= '\'CapHeight\'=>'.$fmetric['CapHeight'].','; 10627 $pfile .= '\'XHeight\'=>'.$fmetric['XHeight'].','; 10628 $pfile .= '\'StemV\'=>'.$fmetric['StemV'].','; 10629 $pfile .= '\'StemH\'=>'.$fmetric['StemH'].','; 10630 $pfile .= '\'AvgWidth\'=>'.$fmetric['AvgWidth'].','; 10631 $pfile .= '\'MaxWidth\'=>'.$fmetric['MaxWidth'].','; 10632 $pfile .= '\'MissingWidth\'=>'.$fmetric['MissingWidth'].''; 10633 $pfile .= ');'."\n"; 10634 $pfile .= '$cw=array('.substr($fmetric['cw'], 1).');'."\n"; 10635 $pfile .= '// --- EOF ---'."\n"; 10636 // store file 10637 $fp = fopen($outpath.$font_name.'.php', 'w'); 10638 fwrite($fp, $pfile); 10639 fclose($fp); 10640 // return TCPDF font name 10641 return $font_name; 10642 } 10643 10653 protected function _getTrueTypeFontSubset($font, $subsetchars) { 10654 ksort($subsetchars); 10655 $offset = 0; // offset position of the font data 10656 if ($this->_getULONG($font, $offset) != 0x10000) { 10657 // sfnt version must be 0x00010000 for TrueType version 1.0. 10658 return $font; 10659 } 10660 $offset += 4; 10661 // get number of tables 10662 $numTables = $this->_getUSHORT($font, $offset); 10663 $offset += 2; 10664 // skip searchRange, entrySelector and rangeShift 10665 $offset += 6; 10666 // tables array 10667 $table = array(); 10668 // for each table 10669 for ($i = 0; $i < $numTables; ++$i) { 10670 // get table info 10671 $tag = substr($font, $offset, 4); 10672 $offset += 4; 10673 $table[$tag] = array(); 10674 $table[$tag]['checkSum'] = $this->_getULONG($font, $offset); 10675 $offset += 4; 10676 $table[$tag]['offset'] = $this->_getULONG($font, $offset); 10677 $offset += 4; 10678 $table[$tag]['length'] = $this->_getULONG($font, $offset); 10679 $offset += 4; 10680 } 10681 // check magicNumber 10682 $offset = $table['head']['offset'] + 12; 10683 if ($this->_getULONG($font, $offset) != 0x5F0F3CF5) { 10684 // magicNumber must be 0x5F0F3CF5 10685 return $font; 10686 } 10687 $offset += 4; 10688 // get offset mode (indexToLocFormat : 0 = short, 1 = long) 10689 $offset = $table['head']['offset'] + 50; 10690 $short_offset = ($this->_getSHORT($font, $offset) == 0); 10691 $offset += 2; 10692 // get the offsets to the locations of the glyphs in the font, relative to the beginning of the glyphData table 10693 $indexToLoc = array(); 10694 $offset = $table['loca']['offset']; 10695 if ($short_offset) { 10696 // short version 10697 $tot_num_glyphs = ($table['loca']['length'] / 2); // numGlyphs + 1 10698 for ($i = 0; $i < $tot_num_glyphs; ++$i) { 10699 $indexToLoc[$i] = $this->_getUSHORT($font, $offset) * 2; 10700 $offset += 2; 10701 } 10702 } else { 10703 // long version 10704 $tot_num_glyphs = ($table['loca']['length'] / 4); // numGlyphs + 1 10705 for ($i = 0; $i < $tot_num_glyphs; ++$i) { 10706 $indexToLoc[$i] = $this->_getULONG($font, $offset); 10707 $offset += 4; 10708 } 10709 } 10710 // get glyphs indexes of chars from cmap table 10711 $subsetglyphs = array(); // glyph IDs on key 10712 $subsetglyphs[0] = true; // character codes that do not correspond to any glyph in the font should be mapped to glyph index 0 10713 $offset = $table['cmap']['offset'] + 2; 10714 $numEncodingTables = $this->_getUSHORT($font, $offset); 10715 $offset += 2; 10716 $encodingTables = array(); 10717 for ($i = 0; $i < $numEncodingTables; ++$i) { 10718 $encodingTables[$i]['platformID'] = $this->_getUSHORT($font, $offset); 10719 $offset += 2; 10720 $encodingTables[$i]['encodingID'] = $this->_getUSHORT($font, $offset); 10721 $offset += 2; 10722 $encodingTables[$i]['offset'] = $this->_getULONG($font, $offset); 10723 $offset += 4; 10724 } 10725 foreach ($encodingTables as $enctable) { 10726 if (($enctable['platformID'] == 3) AND ($enctable['encodingID'] == 0)) { 10727 $modesymbol = true; 10728 } else { 10729 $modesymbol = false; 10730 } 10731 $offset = $table['cmap']['offset'] + $enctable['offset']; 10732 $format = $this->_getUSHORT($font, $offset); 10733 $offset += 2; 10734 switch ($format) { 10735 case 0: { // Format 0: Byte encoding table 10736 $offset += 4; // skip length and version/language 10737 for ($c = 0; $c < 256; ++$c) { 10738 if (isset($subsetchars[$c])) { 10739 $g = $this->_getBYTE($font, $offset); 10740 $subsetglyphs[$g] = true; 10741 } 10742 ++$offset; 10743 } 10744 break; 10745 } 10746 case 2: { // Format 2: High-byte mapping through table 10747 $offset += 4; // skip length and version/language 10748 $numSubHeaders = 0; 10749 for ($i = 0; $i < 256; ++$i) { 10750 // Array that maps high bytes to subHeaders: value is subHeader index * 8. 10751 $subHeaderKeys[$i] = ($this->_getUSHORT($font, $offset) / 8); 10752 $offset += 2; 10753 if ($numSubHeaders < $subHeaderKeys[$i]) { 10754 $numSubHeaders = $subHeaderKeys[$i]; 10755 } 10756 } 10757 // the number of subHeaders is equal to the max of subHeaderKeys + 1 10758 ++$numSubHeaders; 10759 // read subHeader structures 10760 $subHeaders = array(); 10761 $numGlyphIndexArray = 0; 10762 for ($k = 0; $k < $numSubHeaders; ++$k) { 10763 $subHeaders[$k]['firstCode'] = $this->_getUSHORT($font, $offset); 10764 $offset += 2; 10765 $subHeaders[$k]['entryCount'] = $this->_getUSHORT($font, $offset); 10766 $offset += 2; 10767 $subHeaders[$k]['idDelta'] = $this->_getSHORT($font, $offset); 10768 $offset += 2; 10769 $subHeaders[$k]['idRangeOffset'] = $this->_getUSHORT($font, $offset); 10770 $offset += 2; 10771 $subHeaders[$k]['idRangeOffset'] -= (2 + (($numSubHeaders - $k - 1) * 8)); 10772 $subHeaders[$k]['idRangeOffset'] /= 2; 10773 $numGlyphIndexArray += $subHeaders[$k]['entryCount']; 10774 } 10775 for ($k = 0; $k < $numGlyphIndexArray; ++$k) { 10776 $glyphIndexArray[$k] = $this->_getUSHORT($font, $offset); 10777 $offset += 2; 10778 } 10779 for ($i = 0; $i < 256; ++$i) { 10780 $k = $subHeaderKeys[$i]; 10781 if ($k == 0) { 10782 // one byte code 10783 $c = $i; 10784 if (isset($subsetchars[$c])) { 10785 $g = $glyphIndexArray[0]; 10786 $subsetglyphs[$g] = true; 10787 } 10788 } else { 10789 // two bytes code 10790 $start_byte = $subHeaders[$k]['firstCode']; 10791 $end_byte = $start_byte + $subHeaders[$k]['entryCount']; 10792 for ($j = $start_byte; $j < $end_byte; ++$j) { 10793 // combine high and low bytes 10794 $c = (($i << 8) + $j); 10795 if (isset($subsetchars[$c])) { 10796 $idRangeOffset = ($subHeaders[$k]['idRangeOffset'] + $j - $subHeaders[$k]['firstCode']); 10797 $g = $glyphIndexArray[$idRangeOffset]; 10798 $g += ($idDelta[$k] - 65536); 10799 if ($g < 0) { 10800 $g = 0; 10801 } 10802 $subsetglyphs[$g] = true; 10803 } 10804 } 10805 } 10806 } 10807 break; 10808 } 10809 case 4: { // Format 4: Segment mapping to delta values 10810 $length = $this->_getUSHORT($font, $offset); 10811 $offset += 2; 10812 $offset += 2; // skip version/language 10813 $segCount = ($this->_getUSHORT($font, $offset) / 2); 10814 $offset += 2; 10815 $offset += 6; // skip searchRange, entrySelector, rangeShift 10816 $endCount = array(); // array of end character codes for each segment 10817 for ($k = 0; $k < $segCount; ++$k) { 10818 $endCount[$k] = $this->_getUSHORT($font, $offset); 10819 $offset += 2; 10820 } 10821 $offset += 2; // skip reservedPad 10822 $startCount = array(); // array of start character codes for each segment 10823 for ($k = 0; $k < $segCount; ++$k) { 10824 $startCount[$k] = $this->_getUSHORT($font, $offset); 10825 $offset += 2; 10826 } 10827 $idDelta = array(); // delta for all character codes in segment 10828 for ($k = 0; $k < $segCount; ++$k) { 10829 $idDelta[$k] = $this->_getUSHORT($font, $offset); 10830 $offset += 2; 10831 } 10832 $idRangeOffset = array(); // Offsets into glyphIdArray or 0 10833 for ($k = 0; $k < $segCount; ++$k) { 10834 $idRangeOffset[$k] = $this->_getUSHORT($font, $offset); 10835 $offset += 2; 10836 } 10837 $gidlen = ($length / 2) - 8 - (4 * $segCount); 10838 $glyphIdArray = array(); // glyph index array 10839 for ($k = 0; $k < $gidlen; ++$k) { 10840 $glyphIdArray[$k] = $this->_getUSHORT($font, $offset); 10841 $offset += 2; 10842 } 10843 for ($k = 0; $k < $segCount; ++$k) { 10844 for ($c = $startCount[$k]; $c <= $endCount[$k]; ++$c) { 10845 if (isset($subsetchars[$c])) { 10846 if ($idRangeOffset[$k] == 0) { 10847 $g = $c; 10848 } else { 10849 $gid = (($idRangeOffset[$k] / 2) + ($c - $startCount[$k]) - ($segCount - $k)); 10850 $g = $glyphIdArray[$gid]; 10851 } 10852 $g += ($idDelta[$k] - 65536); 10853 if ($g < 0) { 10854 $g = 0; 10855 } 10856 $subsetglyphs[$g] = true; 10857 } 10858 } 10859 } 10860 break; 10861 } 10862 case 6: { // Format 6: Trimmed table mapping 10863 $offset += 4; // skip length and version/language 10864 $firstCode = $this->_getUSHORT($font, $offset); 10865 $offset += 2; 10866 $entryCount = $this->_getUSHORT($font, $offset); 10867 $offset += 2; 10868 for ($k = 0; $k < $entryCount; ++$k) { 10869 $c = ($k + $firstCode); 10870 if (isset($subsetchars[$c])) { 10871 $g = $this->_getUSHORT($font, $offset); 10872 $subsetglyphs[$g] = true; 10873 } 10874 $offset += 2; 10875 } 10876 break; 10877 } 10878 case 8: { // Format 8: Mixed 16-bit and 32-bit coverage 10879 $offset += 10; // skip reserved, length and version/language 10880 for ($k = 0; $k < 8192; ++$k) { 10881 $is32[$k] = $this->_getBYTE($font, $offset); 10882 ++$offset; 10883 } 10884 $nGroups = $this->_getULONG($font, $offset); 10885 $offset += 4; 10886 for ($i = 0; $i < $nGroups; ++$i) { 10887 $startCharCode = $this->_getULONG($font, $offset); 10888 $offset += 4; 10889 $endCharCode = $this->_getULONG($font, $offset); 10890 $offset += 4; 10891 $startGlyphID = $this->_getULONG($font, $offset); 10892 $offset += 4; 10893 for ($k = $startCharCode; $k <= $endCharCode; ++$k) { 10894 $is32idx = floor($c / 8); 10895 if ((isset($is32[$is32idx])) AND (($is32[$is32idx] & (1 << (7 - ($c % 8)))) == 0)) { 10896 $c = $k; 10897 } else { 10898 // 32 bit format 10899 // convert to decimal (http://www.unicode.org/faq//utf_bom.html#utf16-4) 10900 //LEAD_OFFSET = (0xD800 - (0x10000 >> 10)) = 55232 10901 //SURROGATE_OFFSET = (0x10000 - (0xD800 << 10) - 0xDC00) = -56613888 10902 $c = ((55232 + ($k >> 10)) << 10) + (0xDC00 + ($k & 0x3FF)) -56613888; 10903 } 10904 if (isset($subsetchars[$c])) { 10905 $subsetglyphs[$startGlyphID] = true; 10906 } 10907 ++$startGlyphID; 10908 } 10909 } 10910 break; 10911 } 10912 case 10: { // Format 10: Trimmed array 10913 $offset += 10; // skip reserved, length and version/language 10914 $startCharCode = $this->_getULONG($font, $offset); 10915 $offset += 4; 10916 $numChars = $this->_getULONG($font, $offset); 10917 $offset += 4; 10918 for ($k = 0; $k < $numChars; ++$k) { 10919 $c = ($k + $startCharCode); 10920 if (isset($subsetchars[$c])) { 10921 $g = $this->_getUSHORT($font, $offset); 10922 $subsetglyphs[$g] = true; 10923 } 10924 $offset += 2; 10925 } 10926 break; 10927 } 10928 case 12: { // Format 12: Segmented coverage 10929 $offset += 10; // skip length and version/language 10930 $nGroups = $this->_getULONG($font, $offset); 10931 $offset += 4; 10932 for ($k = 0; $k < $nGroups; ++$k) { 10933 $startCharCode = $this->_getULONG($font, $offset); 10934 $offset += 4; 10935 $endCharCode = $this->_getULONG($font, $offset); 10936 $offset += 4; 10937 $startGlyphCode = $this->_getULONG($font, $offset); 10938 $offset += 4; 10939 for ($c = $startCharCode; $c <= $endCharCode; ++$c) { 10940 if (isset($subsetchars[$c])) { 10941 $subsetglyphs[$startGlyphCode] = true; 10942 } 10943 ++$startGlyphCode; 10944 } 10945 } 10946 break; 10947 } 10948 case 13: { // Format 13: Many-to-one range mappings 10949 // to be implemented ... 10950 break; 10951 } 10952 case 14: { // Format 14: Unicode Variation Sequences 10953 // to be implemented ... 10954 break; 10955 } 10956 } 10957 } 10958 // include all parts of composite glyphs 10959 $new_sga = $subsetglyphs; 10960 while (!empty($new_sga)) { 10961 $sga = $new_sga; 10962 $new_sga = array(); 10963 foreach ($sga as $key => $val) { 10964 if (isset($indexToLoc[$key])) { 10965 $offset = ($table['glyf']['offset'] + $indexToLoc[$key]); 10966 $numberOfContours = $this->_getSHORT($font, $offset); 10967 $offset += 2; 10968 if ($numberOfContours < 0) { // composite glyph 10969 $offset += 8; // skip xMin, yMin, xMax, yMax 10970 do { 10971 $flags = $this->_getUSHORT($font, $offset); 10972 $offset += 2; 10973 $glyphIndex = $this->_getUSHORT($font, $offset); 10974 $offset += 2; 10975 if (!isset($subsetglyphs[$glyphIndex])) { 10976 // add missing glyphs 10977 $new_sga[$glyphIndex] = true; 10978 } 10979 // skip some bytes by case 10980 if ($flags & 1) { 10981 $offset += 4; 10982 } else { 10983 $offset += 2; 10984 } 10985 if ($flags & 8) { 10986 $offset += 2; 10987 } elseif ($flags & 64) { 10988 $offset += 4; 10989 } elseif ($flags & 128) { 10990 $offset += 8; 10991 } 10992 } while ($flags & 32); 10993 } 10994 } 10995 } 10996 $subsetglyphs += $new_sga; 10997 } 10998 // sort glyphs by key (and remove duplicates) 10999 ksort($subsetglyphs); 11000 // build new glyf and loca tables 11001 $glyf = ''; 11002 $loca = ''; 11003 $offset = 0; 11004 $glyf_offset = $table['glyf']['offset']; 11005 for ($i = 0; $i < $tot_num_glyphs; ++$i) { 11006 if (isset($subsetglyphs[$i])) { 11007 $length = ($indexToLoc[($i + 1)] - $indexToLoc[$i]); 11008 $glyf .= substr($font, ($glyf_offset + $indexToLoc[$i]), $length); 11009 } else { 11010 $length = 0; 11011 } 11012 if ($short_offset) { 11013 $loca .= pack('n', ($offset / 2)); 11014 } else { 11015 $loca .= pack('N', $offset); 11016 } 11017 $offset += $length; 11018 } 11019 // array of table names to preserve (loca and glyf tables will be added later) 11020 // the cmap table is not needed and shall not be present, since the mapping from character codes to glyph descriptions is provided separately 11021 $table_names = array ('head', 'hhea', 'hmtx', 'maxp', 'cvt ', 'fpgm', 'prep'); // minimum required table names 11022 // get the tables to preserve 11023 $offset = 12; 11024 foreach ($table as $tag => $val) { 11025 if (in_array($tag, $table_names)) { 11026 $table[$tag]['data'] = substr($font, $table[$tag]['offset'], $table[$tag]['length']); 11027 if ($tag == 'head') { 11028 // set the checkSumAdjustment to 0 11029 $table[$tag]['data'] = substr($table[$tag]['data'], 0, 8)."\x0\x0\x0\x0".substr($table[$tag]['data'], 12); 11030 } 11031 $pad = 4 - ($table[$tag]['length'] % 4); 11032 if ($pad != 4) { 11033 // the length of a table must be a multiple of four bytes 11034 $table[$tag]['length'] += $pad; 11035 $table[$tag]['data'] .= str_repeat("\x0", $pad); 11036 } 11037 $table[$tag]['offset'] = $offset; 11038 $offset += $table[$tag]['length']; 11039 // check sum is not changed (so keep the following line commented) 11040 //$table[$tag]['checkSum'] = $this->_getTTFtableChecksum($table[$tag]['data'], $table[$tag]['length']); 11041 } else { 11042 unset($table[$tag]); 11043 } 11044 } 11045 // add loca 11046 $table['loca']['data'] = $loca; 11047 $table['loca']['length'] = strlen($loca); 11048 $pad = 4 - ($table['loca']['length'] % 4); 11049 if ($pad != 4) { 11050 // the length of a table must be a multiple of four bytes 11051 $table['loca']['length'] += $pad; 11052 $table['loca']['data'] .= str_repeat("\x0", $pad); 11053 } 11054 $table['loca']['offset'] = $offset; 11055 $table['loca']['checkSum'] = $this->_getTTFtableChecksum($table['loca']['data'], $table['loca']['length']); 11056 $offset += $table['loca']['length']; 11057 // add glyf 11058 $table['glyf']['data'] = $glyf; 11059 $table['glyf']['length'] = strlen($glyf); 11060 $pad = 4 - ($table['glyf']['length'] % 4); 11061 if ($pad != 4) { 11062 // the length of a table must be a multiple of four bytes 11063 $table['glyf']['length'] += $pad; 11064 $table['glyf']['data'] .= str_repeat("\x0", $pad); 11065 } 11066 $table['glyf']['offset'] = $offset; 11067 $table['glyf']['checkSum'] = $this->_getTTFtableChecksum($table['glyf']['data'], $table['glyf']['length']); 11068 // rebuild font 11069 $font = ''; 11070 $font .= pack('N', 0x10000); // sfnt version 11071 $numTables = count($table); 11072 $font .= pack('n', $numTables); // numTables 11073 $entrySelector = floor(log($numTables, 2)); 11074 $searchRange = pow(2, $entrySelector) * 16; 11075 $rangeShift = ($numTables * 16) - $searchRange; 11076 $font .= pack('n', $searchRange); // searchRange 11077 $font .= pack('n', $entrySelector); // entrySelector 11078 $font .= pack('n', $rangeShift); // rangeShift 11079 $offset = ($numTables * 16); 11080 foreach ($table as $tag => $data) { 11081 $font .= $tag; // tag 11082 $font .= pack('N', $data['checkSum']); // checkSum 11083 $font .= pack('N', ($data['offset'] + $offset)); // offset 11084 $font .= pack('N', $data['length']); // length 11085 } 11086 foreach ($table as $data) { 11087 $font .= $data['data']; 11088 } 11089 // set checkSumAdjustment on head table 11090 $checkSumAdjustment = 0xB1B0AFBA - $this->_getTTFtableChecksum($font, strlen($font)); 11091 $font = substr($font, 0, $table['head']['offset'] + 8).pack('N', $checkSumAdjustment).substr($font, $table['head']['offset'] + 12); 11092 return $font; 11093 } 11094 11104 protected function _getTTFtableChecksum($table, $length) { 11105 $sum = 0; 11106 $tlen = ($length / 4); 11107 $offset = 0; 11108 for ($i = 0; $i < $tlen; ++$i) { 11109 $v = unpack('Ni', substr($table, $offset, 4)); 11110 $sum += $v['i']; 11111 $offset += 4; 11112 } 11113 $sum = unpack('Ni', pack('N', $sum)); 11114 return $sum['i']; 11115 } 11116 11126 protected function _putfontwidths($font, $cidoffset=0) { 11127 ksort($font['cw']); 11128 $rangeid = 0; 11129 $range = array(); 11130 $prevcid = -2; 11131 $prevwidth = -1; 11132 $interval = false; 11133 // for each character 11134 foreach ($font['cw'] as $cid => $width) { 11135 $cid -= $cidoffset; 11136 if ($font['subset'] AND ($cid > 255) AND (!isset($font['subsetchars'][$cid]))) { 11137 // ignore the unused characters (font subsetting) 11138 continue; 11139 } 11140 if ($width != $font['dw']) { 11141 if ($cid == ($prevcid + 1)) { 11142 // consecutive CID 11143 if ($width == $prevwidth) { 11144 if ($width == $range[$rangeid][0]) { 11145 $range[$rangeid][] = $width; 11146 } else { 11147 array_pop($range[$rangeid]); 11148 // new range 11149 $rangeid = $prevcid; 11150 $range[$rangeid] = array(); 11151 $range[$rangeid][] = $prevwidth; 11152 $range[$rangeid][] = $width; 11153 } 11154 $interval = true; 11155 $range[$rangeid]['interval'] = true; 11156 } else { 11157 if ($interval) { 11158 // new range 11159 $rangeid = $cid; 11160 $range[$rangeid] = array(); 11161 $range[$rangeid][] = $width; 11162 } else { 11163 $range[$rangeid][] = $width; 11164 } 11165 $interval = false; 11166 } 11167 } else { 11168 // new range 11169 $rangeid = $cid; 11170 $range[$rangeid] = array(); 11171 $range[$rangeid][] = $width; 11172 $interval = false; 11173 } 11174 $prevcid = $cid; 11175 $prevwidth = $width; 11176 } 11177 } 11178 // optimize ranges 11179 $prevk = -1; 11180 $nextk = -1; 11181 $prevint = false; 11182 foreach ($range as $k => $ws) { 11183 $cws = count($ws); 11184 if (($k == $nextk) AND (!$prevint) AND ((!isset($ws['interval'])) OR ($cws < 4))) { 11185 if (isset($range[$k]['interval'])) { 11186 unset($range[$k]['interval']); 11187 } 11188 $range[$prevk] = array_merge($range[$prevk], $range[$k]); 11189 unset($range[$k]); 11190 } else { 11191 $prevk = $k; 11192 } 11193 $nextk = $k + $cws; 11194 if (isset($ws['interval'])) { 11195 if ($cws > 3) { 11196 $prevint = true; 11197 } else { 11198 $prevint = false; 11199 } 11200 unset($range[$k]['interval']); 11201 --$nextk; 11202 } else { 11203 $prevint = false; 11204 } 11205 } 11206 // output data 11207 $w = ''; 11208 foreach ($range as $k => $ws) { 11209 if (count(array_count_values($ws)) == 1) { 11210 // interval mode is more compact 11211 $w .= ' '.$k.' '.($k + count($ws) - 1).' '.$ws[0]; 11212 } else { 11213 // range mode 11214 $w .= ' '.$k.' [ '.implode(' ', $ws).' ]'; 11215 } 11216 } 11217 return '/W ['.$w.' ]'; 11218 } 11219 11225 protected function _putfonts() { 11226 $nf = $this->n; 11227 foreach ($this->diffs as $diff) { 11228 //Encodings 11229 $this->_newobj(); 11230 $this->_out('<< /Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.'] >>'."\n".'endobj'); 11231 } 11232 $mqr = $this->get_mqr(); 11233 $this->set_mqr(false); 11234 foreach ($this->FontFiles as $file => $info) { 11235 // search and get font file to embedd 11236 $fontdir = $info['fontdir']; 11237 $file = strtolower($file); 11238 $fontfile = ''; 11239 // search files on various directories 11240 if (($fontdir !== false) AND file_exists($fontdir.$file)) { 11241 $fontfile = $fontdir.$file; 11242 } elseif (file_exists($this->_getfontpath().$file)) { 11243 $fontfile = $this->_getfontpath().$file; 11244 } elseif (file_exists($file)) { 11245 $fontfile = $file; 11246 } 11247 if (!$this->empty_string($fontfile)) { 11248 $font = file_get_contents($fontfile); 11249 $compressed = (substr($file, -2) == '.z'); 11250 if ((!$compressed) AND (isset($info['length2']))) { 11251 $header = (ord($font{0}) == 128); 11252 if ($header) { 11253 // strip first binary header 11254 $font = substr($font, 6); 11255 } 11256 if ($header AND (ord($font{$info['length1']}) == 128)) { 11257 // strip second binary header 11258 $font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] + 6)); 11259 } 11260 } elseif ($info['subset'] AND ((!$compressed) OR ($compressed AND function_exists('gzcompress')))) { 11261 if ($compressed) { 11262 // uncompress font 11263 $font = gzuncompress($font); 11264 } 11265 // merge subset characters 11266 $subsetchars = array(); // used chars 11267 foreach ($info['fontkeys'] as $fontkey) { 11268 $fontinfo = $this->getFontBuffer($fontkey); 11269 $subsetchars += $fontinfo['subsetchars']; 11270 } 11271 // rebuild a font subset 11272 $font = $this->_getTrueTypeFontSubset($font, $subsetchars); 11273 // calculate new font length 11274 $info['length1'] = strlen($font); 11275 if ($compressed) { 11276 // recompress font 11277 $font = gzcompress($font); 11278 } 11279 } 11280 $this->_newobj(); 11281 $this->FontFiles[$file]['n'] = $this->n; 11282 $stream = $this->_getrawstream($font); 11283 $out = '<< /Length '.strlen($stream); 11284 if ($compressed) { 11285 $out .= ' /Filter /FlateDecode'; 11286 } 11287 $out .= ' /Length1 '.$info['length1']; 11288 if (isset($info['length2'])) { 11289 $out .= ' /Length2 '.$info['length2'].' /Length3 0'; 11290 } 11291 $out .= ' >>'; 11292 $out .= ' stream'."\n".$stream."\n".'endstream'; 11293 $out .= "\n".'endobj'; 11294 $this->_out($out); 11295 } 11296 } 11297 $this->set_mqr($mqr); 11298 foreach ($this->fontkeys as $k) { 11299 //Font objects 11300 $font = $this->getFontBuffer($k); 11301 $type = $font['type']; 11302 $name = $font['name']; 11303 if ($type == 'core') { 11304 // standard core font 11305 $out = $this->_getobj($this->font_obj_ids[$k])."\n"; 11306 $out .= '<</Type /Font'; 11307 $out .= ' /Subtype /Type1'; 11308 $out .= ' /BaseFont /'.$name; 11309 $out .= ' /Name /F'.$font['i']; 11310 if ((strtolower($name) != 'symbol') AND (strtolower($name) != 'zapfdingbats')) { 11311 $out .= ' /Encoding /WinAnsiEncoding'; 11312 } 11313 if ($k == 'helvetica') { 11314 // add default font for annotations 11315 $this->annotation_fonts[$k] = $font['i']; 11316 } 11317 $out .= ' >>'; 11318 $out .= "\n".'endobj'; 11319 $this->_out($out); 11320 } elseif (($type == 'Type1') OR ($type == 'TrueType')) { 11321 // additional Type1 or TrueType font 11322 $out = $this->_getobj($this->font_obj_ids[$k])."\n"; 11323 $out .= '<</Type /Font'; 11324 $out .= ' /Subtype /'.$type; 11325 $out .= ' /BaseFont /'.$name; 11326 $out .= ' /Name /F'.$font['i']; 11327 $out .= ' /FirstChar 32 /LastChar 255'; 11328 $out .= ' /Widths '.($this->n + 1).' 0 R'; 11329 $out .= ' /FontDescriptor '.($this->n + 2).' 0 R'; 11330 if ($font['enc']) { 11331 if (isset($font['diff'])) { 11332 $out .= ' /Encoding '.($nf + $font['diff']).' 0 R'; 11333 } else { 11334 $out .= ' /Encoding /WinAnsiEncoding'; 11335 } 11336 } 11337 $out .= ' >>'; 11338 $out .= "\n".'endobj'; 11339 $this->_out($out); 11340 // Widths 11341 $this->_newobj(); 11342 $s = '['; 11343 for ($i = 32; $i < 256; ++$i) { 11344 $s .= $font['cw'][$i].' '; 11345 } 11346 $s .= ']'; 11347 $s .= "\n".'endobj'; 11348 $this->_out($s); 11349 //Descriptor 11350 $this->_newobj(); 11351 $s = '<</Type /FontDescriptor /FontName /'.$name; 11352 foreach ($font['desc'] as $fdk => $fdv) { 11353 if (is_float($fdv)) { 11354 $fdv = sprintf('%.3F', $fdv); 11355 } 11356 $s .= ' /'.$fdk.' '.$fdv.''; 11357 } 11358 if (!$this->empty_string($font['file'])) { 11359 $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R'; 11360 } 11361 $s .= '>>'; 11362 $s .= "\n".'endobj'; 11363 $this->_out($s); 11364 } else { 11365 // additional types 11366 $mtd = '_put'.strtolower($type); 11367 if (!method_exists($this, $mtd)) { 11368 $this->Error('Unsupported font type: '.$type); 11369 } 11370 $this->$mtd($font); 11371 } 11372 } 11373 } 11374 11383 protected function _puttruetypeunicode($font) { 11384 $fontname = ''; 11385 if ($font['subset']) { 11386 // change name for font subsetting 11387 $subtag = sprintf('%06u', $font['i']); 11388 $subtag = strtr($subtag, '0123456789', 'ABCDEFGHIJ'); 11389 $fontname .= $subtag.'+'; 11390 } 11391 $fontname .= $font['name']; 11392 // Type0 Font 11393 // A composite font composed of other fonts, organized hierarchically 11394 $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n"; 11395 $out .= '<< /Type /Font'; 11396 $out .= ' /Subtype /Type0'; 11397 $out .= ' /BaseFont /'.$fontname; 11398 $out .= ' /Name /F'.$font['i']; 11399 $out .= ' /Encoding /'.$font['enc']; 11400 $out .= ' /ToUnicode '.($this->n + 1).' 0 R'; 11401 $out .= ' /DescendantFonts ['.($this->n + 2).' 0 R]'; 11402 $out .= ' >>'; 11403 $out .= "\n".'endobj'; 11404 $this->_out($out); 11405 // ToUnicode map for Identity-H 11406 $stream = "/CIDInit /ProcSet findresource begin\n"; 11407 $stream .= "12 dict begin\n"; 11408 $stream .= "begincmap\n"; 11409 $stream .= "/CIDSystemInfo << /Registry (Adobe) /Ordering (UCS) /Supplement 0 >> def\n"; 11410 $stream .= "/CMapName /Adobe-Identity-UCS def\n"; 11411 $stream .= "/CMapType 2 def\n"; 11412 $stream .= "/WMode 0 def\n"; 11413 $stream .= "1 begincodespacerange\n"; 11414 $stream .= "<0000> <FFFF>\n"; 11415 $stream .= "endcodespacerange\n"; 11416 $stream .= "100 beginbfrange\n"; 11417 $stream .= "<0000> <00ff> <0000>\n"; 11418 $stream .= "<0100> <01ff> <0100>\n"; 11419 $stream .= "<0200> <02ff> <0200>\n"; 11420 $stream .= "<0300> <03ff> <0300>\n"; 11421 $stream .= "<0400> <04ff> <0400>\n"; 11422 $stream .= "<0500> <05ff> <0500>\n"; 11423 $stream .= "<0600> <06ff> <0600>\n"; 11424 $stream .= "<0700> <07ff> <0700>\n"; 11425 $stream .= "<0800> <08ff> <0800>\n"; 11426 $stream .= "<0900> <09ff> <0900>\n"; 11427 $stream .= "<0a00> <0aff> <0a00>\n"; 11428 $stream .= "<0b00> <0bff> <0b00>\n"; 11429 $stream .= "<0c00> <0cff> <0c00>\n"; 11430 $stream .= "<0d00> <0dff> <0d00>\n"; 11431 $stream .= "<0e00> <0eff> <0e00>\n"; 11432 $stream .= "<0f00> <0fff> <0f00>\n"; 11433 $stream .= "<1000> <10ff> <1000>\n"; 11434 $stream .= "<1100> <11ff> <1100>\n"; 11435 $stream .= "<1200> <12ff> <1200>\n"; 11436 $stream .= "<1300> <13ff> <1300>\n"; 11437 $stream .= "<1400> <14ff> <1400>\n"; 11438 $stream .= "<1500> <15ff> <1500>\n"; 11439 $stream .= "<1600> <16ff> <1600>\n"; 11440 $stream .= "<1700> <17ff> <1700>\n"; 11441 $stream .= "<1800> <18ff> <1800>\n"; 11442 $stream .= "<1900> <19ff> <1900>\n"; 11443 $stream .= "<1a00> <1aff> <1a00>\n"; 11444 $stream .= "<1b00> <1bff> <1b00>\n"; 11445 $stream .= "<1c00> <1cff> <1c00>\n"; 11446 $stream .= "<1d00> <1dff> <1d00>\n"; 11447 $stream .= "<1e00> <1eff> <1e00>\n"; 11448 $stream .= "<1f00> <1fff> <1f00>\n"; 11449 $stream .= "<2000> <20ff> <2000>\n"; 11450 $stream .= "<2100> <21ff> <2100>\n"; 11451 $stream .= "<2200> <22ff> <2200>\n"; 11452 $stream .= "<2300> <23ff> <2300>\n"; 11453 $stream .= "<2400> <24ff> <2400>\n"; 11454 $stream .= "<2500> <25ff> <2500>\n"; 11455 $stream .= "<2600> <26ff> <2600>\n"; 11456 $stream .= "<2700> <27ff> <2700>\n"; 11457 $stream .= "<2800> <28ff> <2800>\n"; 11458 $stream .= "<2900> <29ff> <2900>\n"; 11459 $stream .= "<2a00> <2aff> <2a00>\n"; 11460 $stream .= "<2b00> <2bff> <2b00>\n"; 11461 $stream .= "<2c00> <2cff> <2c00>\n"; 11462 $stream .= "<2d00> <2dff> <2d00>\n"; 11463 $stream .= "<2e00> <2eff> <2e00>\n"; 11464 $stream .= "<2f00> <2fff> <2f00>\n"; 11465 $stream .= "<3000> <30ff> <3000>\n"; 11466 $stream .= "<3100> <31ff> <3100>\n"; 11467 $stream .= "<3200> <32ff> <3200>\n"; 11468 $stream .= "<3300> <33ff> <3300>\n"; 11469 $stream .= "<3400> <34ff> <3400>\n"; 11470 $stream .= "<3500> <35ff> <3500>\n"; 11471 $stream .= "<3600> <36ff> <3600>\n"; 11472 $stream .= "<3700> <37ff> <3700>\n"; 11473 $stream .= "<3800> <38ff> <3800>\n"; 11474 $stream .= "<3900> <39ff> <3900>\n"; 11475 $stream .= "<3a00> <3aff> <3a00>\n"; 11476 $stream .= "<3b00> <3bff> <3b00>\n"; 11477 $stream .= "<3c00> <3cff> <3c00>\n"; 11478 $stream .= "<3d00> <3dff> <3d00>\n"; 11479 $stream .= "<3e00> <3eff> <3e00>\n"; 11480 $stream .= "<3f00> <3fff> <3f00>\n"; 11481 $stream .= "<4000> <40ff> <4000>\n"; 11482 $stream .= "<4100> <41ff> <4100>\n"; 11483 $stream .= "<4200> <42ff> <4200>\n"; 11484 $stream .= "<4300> <43ff> <4300>\n"; 11485 $stream .= "<4400> <44ff> <4400>\n"; 11486 $stream .= "<4500> <45ff> <4500>\n"; 11487 $stream .= "<4600> <46ff> <4600>\n"; 11488 $stream .= "<4700> <47ff> <4700>\n"; 11489 $stream .= "<4800> <48ff> <4800>\n"; 11490 $stream .= "<4900> <49ff> <4900>\n"; 11491 $stream .= "<4a00> <4aff> <4a00>\n"; 11492 $stream .= "<4b00> <4bff> <4b00>\n"; 11493 $stream .= "<4c00> <4cff> <4c00>\n"; 11494 $stream .= "<4d00> <4dff> <4d00>\n"; 11495 $stream .= "<4e00> <4eff> <4e00>\n"; 11496 $stream .= "<4f00> <4fff> <4f00>\n"; 11497 $stream .= "<5000> <50ff> <5000>\n"; 11498 $stream .= "<5100> <51ff> <5100>\n"; 11499 $stream .= "<5200> <52ff> <5200>\n"; 11500 $stream .= "<5300> <53ff> <5300>\n"; 11501 $stream .= "<5400> <54ff> <5400>\n"; 11502 $stream .= "<5500> <55ff> <5500>\n"; 11503 $stream .= "<5600> <56ff> <5600>\n"; 11504 $stream .= "<5700> <57ff> <5700>\n"; 11505 $stream .= "<5800> <58ff> <5800>\n"; 11506 $stream .= "<5900> <59ff> <5900>\n"; 11507 $stream .= "<5a00> <5aff> <5a00>\n"; 11508 $stream .= "<5b00> <5bff> <5b00>\n"; 11509 $stream .= "<5c00> <5cff> <5c00>\n"; 11510 $stream .= "<5d00> <5dff> <5d00>\n"; 11511 $stream .= "<5e00> <5eff> <5e00>\n"; 11512 $stream .= "<5f00> <5fff> <5f00>\n"; 11513 $stream .= "<6000> <60ff> <6000>\n"; 11514 $stream .= "<6100> <61ff> <6100>\n"; 11515 $stream .= "<6200> <62ff> <6200>\n"; 11516 $stream .= "<6300> <63ff> <6300>\n"; 11517 $stream .= "endbfrange\n"; 11518 $stream .= "100 beginbfrange\n"; 11519 $stream .= "<6400> <64ff> <6400>\n"; 11520 $stream .= "<6500> <65ff> <6500>\n"; 11521 $stream .= "<6600> <66ff> <6600>\n"; 11522 $stream .= "<6700> <67ff> <6700>\n"; 11523 $stream .= "<6800> <68ff> <6800>\n"; 11524 $stream .= "<6900> <69ff> <6900>\n"; 11525 $stream .= "<6a00> <6aff> <6a00>\n"; 11526 $stream .= "<6b00> <6bff> <6b00>\n"; 11527 $stream .= "<6c00> <6cff> <6c00>\n"; 11528 $stream .= "<6d00> <6dff> <6d00>\n"; 11529 $stream .= "<6e00> <6eff> <6e00>\n"; 11530 $stream .= "<6f00> <6fff> <6f00>\n"; 11531 $stream .= "<7000> <70ff> <7000>\n"; 11532 $stream .= "<7100> <71ff> <7100>\n"; 11533 $stream .= "<7200> <72ff> <7200>\n"; 11534 $stream .= "<7300> <73ff> <7300>\n"; 11535 $stream .= "<7400> <74ff> <7400>\n"; 11536 $stream .= "<7500> <75ff> <7500>\n"; 11537 $stream .= "<7600> <76ff> <7600>\n"; 11538 $stream .= "<7700> <77ff> <7700>\n"; 11539 $stream .= "<7800> <78ff> <7800>\n"; 11540 $stream .= "<7900> <79ff> <7900>\n"; 11541 $stream .= "<7a00> <7aff> <7a00>\n"; 11542 $stream .= "<7b00> <7bff> <7b00>\n"; 11543 $stream .= "<7c00> <7cff> <7c00>\n"; 11544 $stream .= "<7d00> <7dff> <7d00>\n"; 11545 $stream .= "<7e00> <7eff> <7e00>\n"; 11546 $stream .= "<7f00> <7fff> <7f00>\n"; 11547 $stream .= "<8000> <80ff> <8000>\n"; 11548 $stream .= "<8100> <81ff> <8100>\n"; 11549 $stream .= "<8200> <82ff> <8200>\n"; 11550 $stream .= "<8300> <83ff> <8300>\n"; 11551 $stream .= "<8400> <84ff> <8400>\n"; 11552 $stream .= "<8500> <85ff> <8500>\n"; 11553 $stream .= "<8600> <86ff> <8600>\n"; 11554 $stream .= "<8700> <87ff> <8700>\n"; 11555 $stream .= "<8800> <88ff> <8800>\n"; 11556 $stream .= "<8900> <89ff> <8900>\n"; 11557 $stream .= "<8a00> <8aff> <8a00>\n"; 11558 $stream .= "<8b00> <8bff> <8b00>\n"; 11559 $stream .= "<8c00> <8cff> <8c00>\n"; 11560 $stream .= "<8d00> <8dff> <8d00>\n"; 11561 $stream .= "<8e00> <8eff> <8e00>\n"; 11562 $stream .= "<8f00> <8fff> <8f00>\n"; 11563 $stream .= "<9000> <90ff> <9000>\n"; 11564 $stream .= "<9100> <91ff> <9100>\n"; 11565 $stream .= "<9200> <92ff> <9200>\n"; 11566 $stream .= "<9300> <93ff> <9300>\n"; 11567 $stream .= "<9400> <94ff> <9400>\n"; 11568 $stream .= "<9500> <95ff> <9500>\n"; 11569 $stream .= "<9600> <96ff> <9600>\n"; 11570 $stream .= "<9700> <97ff> <9700>\n"; 11571 $stream .= "<9800> <98ff> <9800>\n"; 11572 $stream .= "<9900> <99ff> <9900>\n"; 11573 $stream .= "<9a00> <9aff> <9a00>\n"; 11574 $stream .= "<9b00> <9bff> <9b00>\n"; 11575 $stream .= "<9c00> <9cff> <9c00>\n"; 11576 $stream .= "<9d00> <9dff> <9d00>\n"; 11577 $stream .= "<9e00> <9eff> <9e00>\n"; 11578 $stream .= "<9f00> <9fff> <9f00>\n"; 11579 $stream .= "<a000> <a0ff> <a000>\n"; 11580 $stream .= "<a100> <a1ff> <a100>\n"; 11581 $stream .= "<a200> <a2ff> <a200>\n"; 11582 $stream .= "<a300> <a3ff> <a300>\n"; 11583 $stream .= "<a400> <a4ff> <a400>\n"; 11584 $stream .= "<a500> <a5ff> <a500>\n"; 11585 $stream .= "<a600> <a6ff> <a600>\n"; 11586 $stream .= "<a700> <a7ff> <a700>\n"; 11587 $stream .= "<a800> <a8ff> <a800>\n"; 11588 $stream .= "<a900> <a9ff> <a900>\n"; 11589 $stream .= "<aa00> <aaff> <aa00>\n"; 11590 $stream .= "<ab00> <abff> <ab00>\n"; 11591 $stream .= "<ac00> <acff> <ac00>\n"; 11592 $stream .= "<ad00> <adff> <ad00>\n"; 11593 $stream .= "<ae00> <aeff> <ae00>\n"; 11594 $stream .= "<af00> <afff> <af00>\n"; 11595 $stream .= "<b000> <b0ff> <b000>\n"; 11596 $stream .= "<b100> <b1ff> <b100>\n"; 11597 $stream .= "<b200> <b2ff> <b200>\n"; 11598 $stream .= "<b300> <b3ff> <b300>\n"; 11599 $stream .= "<b400> <b4ff> <b400>\n"; 11600 $stream .= "<b500> <b5ff> <b500>\n"; 11601 $stream .= "<b600> <b6ff> <b600>\n"; 11602 $stream .= "<b700> <b7ff> <b700>\n"; 11603 $stream .= "<b800> <b8ff> <b800>\n"; 11604 $stream .= "<b900> <b9ff> <b900>\n"; 11605 $stream .= "<ba00> <baff> <ba00>\n"; 11606 $stream .= "<bb00> <bbff> <bb00>\n"; 11607 $stream .= "<bc00> <bcff> <bc00>\n"; 11608 $stream .= "<bd00> <bdff> <bd00>\n"; 11609 $stream .= "<be00> <beff> <be00>\n"; 11610 $stream .= "<bf00> <bfff> <bf00>\n"; 11611 $stream .= "<c000> <c0ff> <c000>\n"; 11612 $stream .= "<c100> <c1ff> <c100>\n"; 11613 $stream .= "<c200> <c2ff> <c200>\n"; 11614 $stream .= "<c300> <c3ff> <c300>\n"; 11615 $stream .= "<c400> <c4ff> <c400>\n"; 11616 $stream .= "<c500> <c5ff> <c500>\n"; 11617 $stream .= "<c600> <c6ff> <c600>\n"; 11618 $stream .= "<c700> <c7ff> <c700>\n"; 11619 $stream .= "endbfrange\n"; 11620 $stream .= "56 beginbfrange\n"; 11621 $stream .= "<c800> <c8ff> <c800>\n"; 11622 $stream .= "<c900> <c9ff> <c900>\n"; 11623 $stream .= "<ca00> <caff> <ca00>\n"; 11624 $stream .= "<cb00> <cbff> <cb00>\n"; 11625 $stream .= "<cc00> <ccff> <cc00>\n"; 11626 $stream .= "<cd00> <cdff> <cd00>\n"; 11627 $stream .= "<ce00> <ceff> <ce00>\n"; 11628 $stream .= "<cf00> <cfff> <cf00>\n"; 11629 $stream .= "<d000> <d0ff> <d000>\n"; 11630 $stream .= "<d100> <d1ff> <d100>\n"; 11631 $stream .= "<d200> <d2ff> <d200>\n"; 11632 $stream .= "<d300> <d3ff> <d300>\n"; 11633 $stream .= "<d400> <d4ff> <d400>\n"; 11634 $stream .= "<d500> <d5ff> <d500>\n"; 11635 $stream .= "<d600> <d6ff> <d600>\n"; 11636 $stream .= "<d700> <d7ff> <d700>\n"; 11637 $stream .= "<d800> <d8ff> <d800>\n"; 11638 $stream .= "<d900> <d9ff> <d900>\n"; 11639 $stream .= "<da00> <daff> <da00>\n"; 11640 $stream .= "<db00> <dbff> <db00>\n"; 11641 $stream .= "<dc00> <dcff> <dc00>\n"; 11642 $stream .= "<dd00> <ddff> <dd00>\n"; 11643 $stream .= "<de00> <deff> <de00>\n"; 11644 $stream .= "<df00> <dfff> <df00>\n"; 11645 $stream .= "<e000> <e0ff> <e000>\n"; 11646 $stream .= "<e100> <e1ff> <e100>\n"; 11647 $stream .= "<e200> <e2ff> <e200>\n"; 11648 $stream .= "<e300> <e3ff> <e300>\n"; 11649 $stream .= "<e400> <e4ff> <e400>\n"; 11650 $stream .= "<e500> <e5ff> <e500>\n"; 11651 $stream .= "<e600> <e6ff> <e600>\n"; 11652 $stream .= "<e700> <e7ff> <e700>\n"; 11653 $stream .= "<e800> <e8ff> <e800>\n"; 11654 $stream .= "<e900> <e9ff> <e900>\n"; 11655 $stream .= "<ea00> <eaff> <ea00>\n"; 11656 $stream .= "<eb00> <ebff> <eb00>\n"; 11657 $stream .= "<ec00> <ecff> <ec00>\n"; 11658 $stream .= "<ed00> <edff> <ed00>\n"; 11659 $stream .= "<ee00> <eeff> <ee00>\n"; 11660 $stream .= "<ef00> <efff> <ef00>\n"; 11661 $stream .= "<f000> <f0ff> <f000>\n"; 11662 $stream .= "<f100> <f1ff> <f100>\n"; 11663 $stream .= "<f200> <f2ff> <f200>\n"; 11664 $stream .= "<f300> <f3ff> <f300>\n"; 11665 $stream .= "<f400> <f4ff> <f400>\n"; 11666 $stream .= "<f500> <f5ff> <f500>\n"; 11667 $stream .= "<f600> <f6ff> <f600>\n"; 11668 $stream .= "<f700> <f7ff> <f700>\n"; 11669 $stream .= "<f800> <f8ff> <f800>\n"; 11670 $stream .= "<f900> <f9ff> <f900>\n"; 11671 $stream .= "<fa00> <faff> <fa00>\n"; 11672 $stream .= "<fb00> <fbff> <fb00>\n"; 11673 $stream .= "<fc00> <fcff> <fc00>\n"; 11674 $stream .= "<fd00> <fdff> <fd00>\n"; 11675 $stream .= "<fe00> <feff> <fe00>\n"; 11676 $stream .= "<ff00> <ffff> <ff00>\n"; 11677 $stream .= "endbfrange\n"; 11678 $stream .= "endcmap\n"; 11679 $stream .= "CMapName currentdict /CMap defineresource pop\n"; 11680 $stream .= "end\n"; 11681 $stream .= "end"; 11682 // ToUnicode Object 11683 $this->_newobj(); 11684 $stream = ($this->compress) ? gzcompress($stream) : $stream; 11685 $filter = ($this->compress) ? '/Filter /FlateDecode ' : ''; 11686 $stream = $this->_getrawstream($stream); 11687 $this->_out('<<'.$filter.'/Length '.strlen($stream).'>> stream'."\n".$stream."\n".'endstream'."\n".'endobj'); 11688 // CIDFontType2 11689 // A CIDFont whose glyph descriptions are based on TrueType font technology 11690 $oid = $this->_newobj(); 11691 $out = '<< /Type /Font'; 11692 $out .= ' /Subtype /CIDFontType2'; 11693 $out .= ' /BaseFont /'.$fontname; 11694 // A dictionary containing entries that define the character collection of the CIDFont. 11695 $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid); 11696 $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid); 11697 $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement']; 11698 $out .= ' /CIDSystemInfo << '.$cidinfo.' >>'; 11699 $out .= ' /FontDescriptor '.($this->n + 1).' 0 R'; 11700 $out .= ' /DW '.$font['dw']; // default width 11701 $out .= "\n".$this->_putfontwidths($font, 0); 11702 if (isset($font['ctg']) AND (!$this->empty_string($font['ctg']))) { 11703 $out .= "\n".'/CIDToGIDMap '.($this->n + 2).' 0 R'; 11704 } 11705 $out .= ' >>'; 11706 $out .= "\n".'endobj'; 11707 $this->_out($out); 11708 // Font descriptor 11709 // A font descriptor describing the CIDFont default metrics other than its glyph widths 11710 $this->_newobj(); 11711 $out = '<< /Type /FontDescriptor'; 11712 $out .= ' /FontName /'.$fontname; 11713 foreach ($font['desc'] as $key => $value) { 11714 if (is_float($value)) { 11715 $value = sprintf('%.3F', $value); 11716 } 11717 $out .= ' /'.$key.' '.$value; 11718 } 11719 $fontdir = false; 11720 if (!$this->empty_string($font['file'])) { 11721 // A stream containing a TrueType font 11722 $out .= ' /FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R'; 11723 $fontdir = $this->FontFiles[$font['file']]['fontdir']; 11724 } 11725 $out .= ' >>'; 11726 $out .= "\n".'endobj'; 11727 $this->_out($out); 11728 if (isset($font['ctg']) AND (!$this->empty_string($font['ctg']))) { 11729 $this->_newobj(); 11730 // Embed CIDToGIDMap 11731 // A specification of the mapping from CIDs to glyph indices 11732 // search and get CTG font file to embedd 11733 $ctgfile = strtolower($font['ctg']); 11734 // search and get ctg font file to embedd 11735 $fontfile = ''; 11736 // search files on various directories 11737 if (($fontdir !== false) AND file_exists($fontdir.$ctgfile)) { 11738 $fontfile = $fontdir.$ctgfile; 11739 } elseif (file_exists($this->_getfontpath().$ctgfile)) { 11740 $fontfile = $this->_getfontpath().$ctgfile; 11741 } elseif (file_exists($ctgfile)) { 11742 $fontfile = $ctgfile; 11743 } 11744 if ($this->empty_string($fontfile)) { 11745 $this->Error('Font file not found: '.$ctgfile); 11746 } 11747 $stream = $this->_getrawstream(file_get_contents($fontfile)); 11748 $out = '<< /Length '.strlen($stream).''; 11749 if (substr($fontfile, -2) == '.z') { // check file extension 11750 // Decompresses data encoded using the public-domain 11751 // zlib/deflate compression method, reproducing the 11752 // original text or binary data 11753 $out .= ' /Filter /FlateDecode'; 11754 } 11755 $out .= ' >>'; 11756 $out .= ' stream'."\n".$stream."\n".'endstream'; 11757 $out .= "\n".'endobj'; 11758 $this->_out($out); 11759 } 11760 } 11761 11770 protected function _putcidfont0($font) { 11771 $cidoffset = 0; 11772 if (!isset($font['cw'][1])) { 11773 $cidoffset = 31; 11774 } 11775 if (isset($font['cidinfo']['uni2cid'])) { 11776 // convert unicode to cid. 11777 $uni2cid = $font['cidinfo']['uni2cid']; 11778 $cw = array(); 11779 foreach ($font['cw'] as $uni => $width) { 11780 if (isset($uni2cid[$uni])) { 11781 $cw[($uni2cid[$uni] + $cidoffset)] = $width; 11782 } elseif ($uni < 256) { 11783 $cw[$uni] = $width; 11784 } // else unknown character 11785 } 11786 $font = array_merge($font, array('cw' => $cw)); 11787 } 11788 $name = $font['name']; 11789 $enc = $font['enc']; 11790 if ($enc) { 11791 $longname = $name.'-'.$enc; 11792 } else { 11793 $longname = $name; 11794 } 11795 $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n"; 11796 $out .= '<</Type /Font'; 11797 $out .= ' /Subtype /Type0'; 11798 $out .= ' /BaseFont /'.$longname; 11799 $out .= ' /Name /F'.$font['i']; 11800 if ($enc) { 11801 $out .= ' /Encoding /'.$enc; 11802 } 11803 $out .= ' /DescendantFonts ['.($this->n + 1).' 0 R]'; 11804 $out .= ' >>'; 11805 $out .= "\n".'endobj'; 11806 $this->_out($out); 11807 $oid = $this->_newobj(); 11808 $out = '<</Type /Font'; 11809 $out .= ' /Subtype /CIDFontType0'; 11810 $out .= ' /BaseFont /'.$name; 11811 $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid); 11812 $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid); 11813 $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement']; 11814 $out .= ' /CIDSystemInfo <<'.$cidinfo.'>>'; 11815 $out .= ' /FontDescriptor '.($this->n + 1).' 0 R'; 11816 $out .= ' /DW '.$font['dw']; 11817 $out .= "\n".$this->_putfontwidths($font, $cidoffset); 11818 $out .= ' >>'; 11819 $out .= "\n".'endobj'; 11820 $this->_out($out); 11821 $this->_newobj(); 11822 $s = '<</Type /FontDescriptor /FontName /'.$name; 11823 foreach ($font['desc'] as $k => $v) { 11824 if ($k != 'Style') { 11825 if (is_float($v)) { 11826 $v = sprintf('%.3F', $v); 11827 } 11828 $s .= ' /'.$k.' '.$v.''; 11829 } 11830 } 11831 $s .= '>>'; 11832 $s .= "\n".'endobj'; 11833 $this->_out($s); 11834 } 11835 11840 protected function _putimages() { 11841 $filter = ($this->compress) ? '/Filter /FlateDecode ' : ''; 11842 foreach ($this->imagekeys as $file) { 11843 $info = $this->getImageBuffer($file); 11844 // set object for alternate images array 11845 if ((!$this->pdfa_mode) AND isset($info['altimgs']) AND !empty($info['altimgs'])) { 11846 $altoid = $this->_newobj(); 11847 $out = '['; 11848 foreach ($info['altimgs'] as $altimage) { 11849 if (isset($this->xobjects['I'.$altimage[0]]['n'])) { 11850 $out .= ' << /Image '.$this->xobjects['I'.$altimage[0]]['n'].' 0 R'; 11851 $out .= ' /DefaultForPrinting'; 11852 if ($altimage[1] === true) { 11853 $out .= ' true'; 11854 } else { 11855 $out .= ' false'; 11856 } 11857 $out .= ' >>'; 11858 } 11859 } 11860 $out .= ' ]'; 11861 $out .= "\n".'endobj'; 11862 $this->_out($out); 11863 } 11864 // set image object 11865 $oid = $this->_newobj(); 11866 $this->xobjects['I'.$info['i']] = array('n' => $oid); 11867 $this->setImageSubBuffer($file, 'n', $this->n); 11868 $out = '<</Type /XObject'; 11869 $out .= ' /Subtype /Image'; 11870 $out .= ' /Width '.$info['w']; 11871 $out .= ' /Height '.$info['h']; 11872 if (array_key_exists('masked', $info)) { 11873 $out .= ' /SMask '.($this->n - 1).' 0 R'; 11874 } 11875 // set color space 11876 $icc = false; 11877 if (isset($info['icc']) AND ($info['icc'] !== false)) { 11878 // ICC Colour Space 11879 $icc = true; 11880 $out .= ' /ColorSpace [/ICCBased '.($this->n + 1).' 0 R]'; 11881 } elseif ($info['cs'] == 'Indexed') { 11882 // Indexed Colour Space 11883 $out .= ' /ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n + 1).' 0 R]'; 11884 } else { 11885 // Device Colour Space 11886 $out .= ' /ColorSpace /'.$info['cs']; 11887 } 11888 if ($info['cs'] == 'DeviceCMYK') { 11889 $out .= ' /Decode [1 0 1 0 1 0 1 0]'; 11890 } 11891 $out .= ' /BitsPerComponent '.$info['bpc']; 11892 if (isset($altoid) AND ($altoid > 0)) { 11893 // reference to alternate images dictionary 11894 $out .= ' /Alternates '.$altoid.' 0 R'; 11895 } 11896 if (isset($info['exurl']) AND !empty($info['exurl'])) { 11897 // external stream 11898 $out .= ' /Length 0'; 11899 $out .= ' /F << /FS /URL /F '.$this->_datastring($info['exurl'], $oid).' >>'; 11900 if (isset($info['f'])) { 11901 $out .= ' /FFilter /'.$info['f']; 11902 } 11903 $out .= ' >>'; 11904 $out .= ' stream'."\n".'endstream'; 11905 } else { 11906 if (isset($info['f'])) { 11907 $out .= ' /Filter /'.$info['f']; 11908 } 11909 if (isset($info['parms'])) { 11910 $out .= ' '.$info['parms']; 11911 } 11912 if (isset($info['trns']) AND is_array($info['trns'])) { 11913 $trns = ''; 11914 $count_info = count($info['trns']); 11915 for ($i=0; $i < $count_info; ++$i) { 11916 $trns .= $info['trns'][$i].' '.$info['trns'][$i].' '; 11917 } 11918 $out .= ' /Mask ['.$trns.']'; 11919 } 11920 $stream = $this->_getrawstream($info['data']); 11921 $out .= ' /Length '.strlen($stream).' >>'; 11922 $out .= ' stream'."\n".$stream."\n".'endstream'; 11923 } 11924 $out .= "\n".'endobj'; 11925 $this->_out($out); 11926 if ($icc) { 11927 // ICC colour profile 11928 $this->_newobj(); 11929 $icc = ($this->compress) ? gzcompress($info['icc']) : $info['icc']; 11930 $icc = $this->_getrawstream($icc); 11931 $this->_out('<</N '.$info['ch'].' /Alternate /'.$info['cs'].' '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj'); 11932 } elseif ($info['cs'] == 'Indexed') { 11933 // colour palette 11934 $this->_newobj(); 11935 $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal']; 11936 $pal = $this->_getrawstream($pal); 11937 $this->_out('<<'.$filter.'/Length '.strlen($pal).'>> stream'."\n".$pal."\n".'endstream'."\n".'endobj'); 11938 } 11939 } 11940 } 11941 11949 protected function _putxobjects() { 11950 foreach ($this->xobjects as $key => $data) { 11951 if (isset($data['outdata'])) { 11952 $stream = trim($data['outdata']); 11953 $out = $this->_getobj($data['n'])."\n"; 11954 $out .= '<<'; 11955 $out .= ' /Type /XObject'; 11956 $out .= ' /Subtype /Form'; 11957 $out .= ' /FormType 1'; 11958 if ($this->compress) { 11959 $stream = gzcompress($stream); 11960 $out .= ' /Filter /FlateDecode'; 11961 } 11962 $out .= sprintf(' /BBox [%.2F %.2F %.2F %.2F]', ($data['x'] * $this->k), (-$data['y'] * $this->k), (($data['w'] + $data['x']) * $this->k), (($data['h'] - $data['y']) * $this->k)); 11963 $out .= ' /Matrix [1 0 0 1 0 0]'; 11964 $out .= ' /Resources <<'; 11965 $out .= ' /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'; 11966 if (!$this->pdfa_mode) { 11967 // transparency 11968 if (isset($data['extgstates']) AND !empty($data['extgstates'])) { 11969 $out .= ' /ExtGState <<'; 11970 foreach ($data['extgstates'] as $k => $extgstate) { 11971 if (isset($this->extgstates[$k]['name'])) { 11972 $out .= ' /'.$this->extgstates[$k]['name']; 11973 } else { 11974 $out .= ' /GS'.$k; 11975 } 11976 $out .= ' '.$this->extgstates[$k]['n'].' 0 R'; 11977 } 11978 $out .= ' >>'; 11979 } 11980 if (isset($data['gradients']) AND !empty($data['gradients'])) { 11981 $gp = ''; 11982 $gs = ''; 11983 foreach ($data['gradients'] as $id => $grad) { 11984 // gradient patterns 11985 $gp .= ' /p'.$id.' '.$this->gradients[$id]['pattern'].' 0 R'; 11986 // gradient shadings 11987 $gs .= ' /Sh'.$id.' '.$this->gradients[$id]['id'].' 0 R'; 11988 } 11989 $out .= ' /Pattern <<'.$gp.' >>'; 11990 $out .= ' /Shading <<'.$gs.' >>'; 11991 } 11992 } 11993 // spot colors 11994 if (isset($data['spot_colors']) AND !empty($data['spot_colors'])) { 11995 $out .= ' /ColorSpace <<'; 11996 foreach ($data['spot_colors'] as $name => $color) { 11997 $out .= ' /CS'.$color['i'].' '.$this->spot_colors[$name]['n'].' 0 R'; 11998 } 11999 $out .= ' >>'; 12000 } 12001 // fonts 12002 if (!empty($data['fonts'])) { 12003 $out .= ' /Font <<'; 12004 foreach ($data['fonts'] as $fontkey => $fontid) { 12005 $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R'; 12006 } 12007 $out .= ' >>'; 12008 } 12009 // images or nested xobjects 12010 if (!empty($data['images']) OR !empty($data['xobjects'])) { 12011 $out .= ' /XObject <<'; 12012 foreach ($data['images'] as $imgid) { 12013 $out .= ' /I'.$imgid.' '.$this->xobjects['I'.$imgid]['n'].' 0 R'; 12014 } 12015 foreach ($data['xobjects'] as $sub_id => $sub_objid) { 12016 $out .= ' /'.$sub_id.' '.$sub_objid['n'].' 0 R'; 12017 } 12018 $out .= ' >>'; 12019 } 12020 $out .= ' >>'; //end resources 12021 if (isset($data['group']) AND ($data['group'] !== false)) { 12022 // set transparency group 12023 $out .= ' /Group << /Type /Group /S /Transparency'; 12024 if (is_array($data['group'])) { 12025 if (isset($data['group']['CS']) AND !empty($data['group']['CS'])) { 12026 $out .= ' /CS /'.$data['group']['CS']; 12027 } 12028 if (isset($data['group']['I'])) { 12029 $out .= ' /I /'.($data['group']['I']===true?'true':'false'); 12030 } 12031 if (isset($data['group']['K'])) { 12032 $out .= ' /K /'.($data['group']['K']===true?'true':'false'); 12033 } 12034 } 12035 $out .= ' >>'; 12036 } 12037 $stream = $this->_getrawstream($stream, $data['n']); 12038 $out .= ' /Length '.strlen($stream); 12039 $out .= ' >>'; 12040 $out .= ' stream'."\n".$stream."\n".'endstream'; 12041 $out .= "\n".'endobj'; 12042 $this->_out($out); 12043 } 12044 } 12045 } 12046 12052 protected function _putspotcolors() { 12053 foreach ($this->spot_colors as $name => $color) { 12054 $this->_newobj(); 12055 $this->spot_colors[$name]['n'] = $this->n; 12056 $out = '[/Separation /'.str_replace(' ', '#20', $name); 12057 $out .= ' /DeviceCMYK <<'; 12058 $out .= ' /Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0]'; 12059 $out .= ' '.sprintf('/C1 [%.4F %.4F %.4F %.4F] ', ($color['c'] / 100), ($color['m'] / 100), ($color['y'] / 100), ($color['k'] / 100)); 12060 $out .= ' /FunctionType 2 /Domain [0 1] /N 1>>]'; 12061 $out .= "\n".'endobj'; 12062 $this->_out($out); 12063 } 12064 } 12065 12072 protected function _getxobjectdict() { 12073 $out = ''; 12074 foreach ($this->xobjects as $id => $objid) { 12075 $out .= ' /'.$id.' '.$objid['n'].' 0 R'; 12076 } 12077 return $out; 12078 } 12079 12084 protected function _putresourcedict() { 12085 $out = $this->_getobj(2)."\n"; 12086 $out .= '<< /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'; 12087 $out .= ' /Font <<'; 12088 foreach ($this->fontkeys as $fontkey) { 12089 $font = $this->getFontBuffer($fontkey); 12090 $out .= ' /F'.$font['i'].' '.$font['n'].' 0 R'; 12091 } 12092 $out .= ' >>'; 12093 $out .= ' /XObject <<'; 12094 $out .= $this->_getxobjectdict(); 12095 $out .= ' >>'; 12096 // layers 12097 if (!empty($this->pdflayers)) { 12098 $out .= ' /Properties <<'; 12099 foreach ($this->pdflayers as $layer) { 12100 $out .= ' /'.$layer['layer'].' '.$layer['objid'].' 0 R'; 12101 } 12102 $out .= ' >>'; 12103 } 12104 if (!$this->pdfa_mode) { 12105 // transparency 12106 if (isset($this->extgstates) AND !empty($this->extgstates)) { 12107 $out .= ' /ExtGState <<'; 12108 foreach ($this->extgstates as $k => $extgstate) { 12109 if (isset($extgstate['name'])) { 12110 $out .= ' /'.$extgstate['name']; 12111 } else { 12112 $out .= ' /GS'.$k; 12113 } 12114 $out .= ' '.$extgstate['n'].' 0 R'; 12115 } 12116 $out .= ' >>'; 12117 } 12118 if (isset($this->gradients) AND !empty($this->gradients)) { 12119 $gp = ''; 12120 $gs = ''; 12121 foreach ($this->gradients as $id => $grad) { 12122 // gradient patterns 12123 $gp .= ' /p'.$id.' '.$grad['pattern'].' 0 R'; 12124 // gradient shadings 12125 $gs .= ' /Sh'.$id.' '.$grad['id'].' 0 R'; 12126 } 12127 $out .= ' /Pattern <<'.$gp.' >>'; 12128 $out .= ' /Shading <<'.$gs.' >>'; 12129 } 12130 } 12131 // spot colors 12132 if (isset($this->spot_colors) AND !empty($this->spot_colors)) { 12133 $out .= ' /ColorSpace <<'; 12134 foreach ($this->spot_colors as $color) { 12135 $out .= ' /CS'.$color['i'].' '.$color['n'].' 0 R'; 12136 } 12137 $out .= ' >>'; 12138 } 12139 $out .= ' >>'; 12140 $out .= "\n".'endobj'; 12141 $this->_out($out); 12142 } 12143 12148 protected function _putresources() { 12149 $this->_putextgstates(); 12150 $this->_putocg(); 12151 $this->_putfonts(); 12152 $this->_putimages(); 12153 $this->_putspotcolors(); 12154 $this->_putshaders(); 12155 $this->_putxobjects(); 12156 $this->_putresourcedict(); 12157 $this->_putdests(); 12158 $this->_putbookmarks(); 12159 $this->_putEmbeddedFiles(); 12160 $this->_putannotsobjs(); 12161 $this->_putjavascript(); 12162 $this->_putencryption(); 12163 } 12164 12171 protected function _putinfo() { 12172 $oid = $this->_newobj(); 12173 $out = '<<'; 12174 // store current isunicode value 12175 $prev_isunicode = $this->isunicode; 12176 if ($this->docinfounicode) { 12177 $this->isunicode = true; 12178 } 12179 if (!$this->empty_string($this->title)) { 12180 // The document's title. 12181 $out .= ' /Title '.$this->_textstring($this->title, $oid); 12182 } 12183 if (!$this->empty_string($this->author)) { 12184 // The name of the person who created the document. 12185 $out .= ' /Author '.$this->_textstring($this->author, $oid); 12186 } 12187 if (!$this->empty_string($this->subject)) { 12188 // The subject of the document. 12189 $out .= ' /Subject '.$this->_textstring($this->subject, $oid); 12190 } 12191 if (!$this->empty_string($this->keywords)) { 12192 // Keywords associated with the document. 12193 $out .= ' /Keywords '.$this->_textstring($this->keywords.' TCPDF', $oid); 12194 } 12195 if (!$this->empty_string($this->creator)) { 12196 // If the document was converted to PDF from another format, the name of the conforming product that created the original document from which it was converted. 12197 $out .= ' /Creator '.$this->_textstring($this->creator, $oid); 12198 } 12199 // restore previous isunicode value 12200 $this->isunicode = $prev_isunicode; 12201 // default producer 12202 $out .= ' /Producer '.$this->_textstring("\x54\x43\x50\x44\x46\x20".$this->tcpdf_version."\x20\x28\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67\x29", $oid); 12203 // The date and time the document was created, in human-readable form 12204 $out .= ' /CreationDate '.$this->_datestring(); 12205 // The date and time the document was most recently modified, in human-readable form 12206 $out .= ' /ModDate '.$this->_datestring(); 12207 // A name object indicating whether the document has been modified to include trapping information 12208 $out .= ' /Trapped /False'; 12209 $out .= ' >>'; 12210 $out .= "\n".'endobj'; 12211 $this->_out($out); 12212 return $oid; 12213 } 12214 12222 public function setExtraXMP($xmp) { 12223 $this->custom_xmp = $xmp; 12224 } 12225 12232 protected function _putXMP() { 12233 $oid = $this->_newobj(); 12234 // store current isunicode value 12235 $prev_isunicode = $this->isunicode; 12236 $this->isunicode = true; 12237 $prev_encrypted = $this->encrypted; 12238 $this->encrypted = false; 12239 // set XMP data 12240 $xmp = '<?xpacket begin="'.$this->unichr(0xfeff).'" id="W5M0MpCehiHzreSzNTczkc9d"?>'."\n"; 12241 $xmp .= '<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 4.2.1-c043 52.372728, 2009/01/18-15:08:04">'."\n"; 12242 $xmp .= "\t".'<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">'."\n"; 12243 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">'."\n"; 12244 $xmp .= "\t\t\t".'<dc:format>application/pdf</dc:format>'."\n"; 12245 $xmp .= "\t\t\t".'<dc:title>'."\n"; 12246 $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n"; 12247 $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.$this->_escapeXML($this->title).'</rdf:li>'."\n"; 12248 $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n"; 12249 $xmp .= "\t\t\t".'</dc:title>'."\n"; 12250 $xmp .= "\t\t\t".'<dc:creator>'."\n"; 12251 $xmp .= "\t\t\t\t".'<rdf:Seq>'."\n"; 12252 $xmp .= "\t\t\t\t\t".'<rdf:li>'.$this->_escapeXML($this->author).'</rdf:li>'."\n"; 12253 $xmp .= "\t\t\t\t".'</rdf:Seq>'."\n"; 12254 $xmp .= "\t\t\t".'</dc:creator>'."\n"; 12255 $xmp .= "\t\t\t".'<dc:description>'."\n"; 12256 $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n"; 12257 $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.$this->_escapeXML($this->subject).'</rdf:li>'."\n"; 12258 $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n"; 12259 $xmp .= "\t\t\t".'</dc:description>'."\n"; 12260 $xmp .= "\t\t\t".'<dc:subject>'."\n"; 12261 $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n"; 12262 $xmp .= "\t\t\t\t\t".'<rdf:li>'.$this->_escapeXML($this->keywords).'</rdf:li>'."\n"; 12263 $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n"; 12264 $xmp .= "\t\t\t".'</dc:subject>'."\n"; 12265 $xmp .= "\t\t".'</rdf:Description>'."\n"; 12266 // convert date format 12267 $docdate = substr($this->doc_date, 0, 4).'-'.substr($this->doc_date, 4, 2).'-'.substr($this->doc_date, 6, 2); 12268 $docdate .= 'T'.substr($this->doc_date, 8, 2).':'.substr($this->doc_date, 10, 2).':'.substr($this->doc_date, 12, 2); 12269 $docdate .= '+'.substr($this->doc_date, 15, 2).':'.substr($this->doc_date, 18, 2); 12270 $docdate = $this->_escapeXML($docdate); 12271 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/">'."\n"; 12272 $xmp .= "\t\t\t".'<xmp:CreateDate>'.$docdate.'</xmp:CreateDate>'."\n"; 12273 $xmp .= "\t\t\t".'<xmp:CreatorTool>TCPDF</xmp:CreatorTool>'."\n"; 12274 $xmp .= "\t\t\t".'<xmp:ModifyDate>'.$docdate.'</xmp:ModifyDate>'."\n"; 12275 $xmp .= "\t\t\t".'<xmp:MetadataDate>'.$docdate.'</xmp:MetadataDate>'."\n"; 12276 $xmp .= "\t\t".'</rdf:Description>'."\n"; 12277 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdf="http://ns.adobe.com/pdf/1.3/">'."\n"; 12278 $xmp .= "\t\t\t".'<pdf:Keywords>'.$this->_escapeXML($this->keywords).' TCPDF</pdf:Keywords>'."\n"; 12279 $xmp .= "\t\t\t".'<pdf:Producer>'.$this->_escapeXML("\x54\x43\x50\x44\x46\x20".$this->tcpdf_version."\x20\x28\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67\x29").'</pdf:Producer>'."\n"; 12280 $xmp .= "\t\t".'</rdf:Description>'."\n"; 12281 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/">'."\n"; 12282 $uuid = 'uuid:'.substr($this->file_id, 0, 8).'-'.substr($this->file_id, 8, 4).'-'.substr($this->file_id, 12, 4).'-'.substr($this->file_id, 16, 4).'-'.substr($this->file_id, 20, 12); 12283 $xmp .= "\t\t\t".'<xmpMM:DocumentID>'.$uuid.'</xmpMM:DocumentID>'."\n"; 12284 $xmp .= "\t\t\t".'<xmpMM:InstanceID>'.$uuid.'</xmpMM:InstanceID>'."\n"; 12285 $xmp .= "\t\t".'</rdf:Description>'."\n"; 12286 if ($this->pdfa_mode) { 12287 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/">'."\n"; 12288 $xmp .= "\t\t\t".'<pdfaid:part>1</pdfaid:part>'."\n"; 12289 $xmp .= "\t\t\t".'<pdfaid:conformance>B</pdfaid:conformance>'."\n"; 12290 $xmp .= "\t\t".'</rdf:Description>'."\n"; 12291 } 12292 // XMP extension schemas 12293 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdfaExtension="http://www.aiim.org/pdfa/ns/extension/" xmlns:pdfaSchema="http://www.aiim.org/pdfa/ns/schema#" xmlns:pdfaProperty="http://www.aiim.org/pdfa/ns/property#">'."\n"; 12294 $xmp .= "\t\t\t".'<pdfaExtension:schemas>'."\n"; 12295 $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n"; 12296 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; 12297 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/pdf/1.3/</pdfaSchema:namespaceURI>'."\n"; 12298 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdf</pdfaSchema:prefix>'."\n"; 12299 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>Adobe PDF Schema</pdfaSchema:schema>'."\n"; 12300 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n"; 12301 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; 12302 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/xap/1.0/mm/</pdfaSchema:namespaceURI>'."\n"; 12303 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>xmpMM</pdfaSchema:prefix>'."\n"; 12304 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>XMP Media Management Schema</pdfaSchema:schema>'."\n"; 12305 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n"; 12306 $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n"; 12307 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; 12308 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n"; 12309 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>UUID based identifier for specific incarnation of a document</pdfaProperty:description>'."\n"; 12310 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>InstanceID</pdfaProperty:name>'."\n"; 12311 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>URI</pdfaProperty:valueType>'."\n"; 12312 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n"; 12313 $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n"; 12314 $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n"; 12315 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n"; 12316 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; 12317 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://www.aiim.org/pdfa/ns/id/</pdfaSchema:namespaceURI>'."\n"; 12318 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdfaid</pdfaSchema:prefix>'."\n"; 12319 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>PDF/A ID Schema</pdfaSchema:schema>'."\n"; 12320 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n"; 12321 $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n"; 12322 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; 12323 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n"; 12324 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Part of PDF/A standard</pdfaProperty:description>'."\n"; 12325 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>part</pdfaProperty:name>'."\n"; 12326 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Integer</pdfaProperty:valueType>'."\n"; 12327 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n"; 12328 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; 12329 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n"; 12330 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Amendment of PDF/A standard</pdfaProperty:description>'."\n"; 12331 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>amd</pdfaProperty:name>'."\n"; 12332 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n"; 12333 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n"; 12334 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; 12335 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n"; 12336 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Conformance level of PDF/A standard</pdfaProperty:description>'."\n"; 12337 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>conformance</pdfaProperty:name>'."\n"; 12338 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n"; 12339 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n"; 12340 $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n"; 12341 $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n"; 12342 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n"; 12343 $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n"; 12344 $xmp .= "\t\t\t".'</pdfaExtension:schemas>'."\n"; 12345 $xmp .= "\t\t".'</rdf:Description>'."\n"; 12346 $xmp .= "\t".'</rdf:RDF>'."\n"; 12347 $xmp .= $this->custom_xmp; 12348 $xmp .= '</x:xmpmeta>'."\n"; 12349 $xmp .= '<?xpacket end="w"?>'; 12350 $out = '<< /Type /Metadata /Subtype /XML /Length '.strlen($xmp).' >> stream'."\n".$xmp."\n".'endstream'."\n".'endobj'; 12351 // restore previous isunicode value 12352 $this->isunicode = $prev_isunicode; 12353 $this->encrypted = $prev_encrypted; 12354 $this->_out($out); 12355 return $oid; 12356 } 12357 12363 protected function _putcatalog() { 12364 // put XMP 12365 $xmpobj = $this->_putXMP(); 12366 // if required, add standard sRGB_IEC61966-2.1 blackscaled ICC colour profile 12367 if ($this->pdfa_mode OR $this->force_srgb) { 12368 $iccobj = $this->_newobj(); 12369 $icc = file_get_contents(dirname(__FILE__).'/sRGB.icc'); 12370 $filter = ''; 12371 if ($this->compress) { 12372 $filter = ' /Filter /FlateDecode'; 12373 $icc = gzcompress($icc); 12374 } 12375 $icc = $this->_getrawstream($icc); 12376 $this->_out('<</N 3 '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj'); 12377 } 12378 // start catalog 12379 $oid = $this->_newobj(); 12380 $out = '<< /Type /Catalog'; 12381 $out .= ' /Version /'.$this->PDFVersion; 12382 //$out .= ' /Extensions <<>>'; 12383 $out .= ' /Pages 1 0 R'; 12384 //$out .= ' /PageLabels ' //...; 12385 $out .= ' /Names <<'; 12386 if ((!$this->pdfa_mode) AND ((!empty($this->javascript)) OR (!empty($this->js_objects)))) { 12387 $out .= ' /JavaScript '.($this->n_js).' 0 R'; 12388 } 12389 $out .= ' >>'; 12390 if (!empty($this->dests)) { 12391 $out .= ' /Dests '.$this->n_dests.' 0 R'; 12392 } 12393 $out .= $this->_putviewerpreferences(); 12394 if (isset($this->LayoutMode) AND (!$this->empty_string($this->LayoutMode))) { 12395 $out .= ' /PageLayout /'.$this->LayoutMode; 12396 } 12397 if (isset($this->PageMode) AND (!$this->empty_string($this->PageMode))) { 12398 $out .= ' /PageMode /'.$this->PageMode; 12399 } 12400 if (count($this->outlines) > 0) { 12401 $out .= ' /Outlines '.$this->OutlineRoot.' 0 R'; 12402 $out .= ' /PageMode /UseOutlines'; 12403 } 12404 //$out .= ' /Threads []'; 12405 if ($this->ZoomMode == 'fullpage') { 12406 $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /Fit]'; 12407 } elseif ($this->ZoomMode == 'fullwidth') { 12408 $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /FitH null]'; 12409 } elseif ($this->ZoomMode == 'real') { 12410 $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null 1]'; 12411 } elseif (!is_string($this->ZoomMode)) { 12412 $out .= sprintf(' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null %.2F]', ($this->ZoomMode / 100)); 12413 } 12414 //$out .= ' /AA <<>>'; 12415 //$out .= ' /URI <<>>'; 12416 $out .= ' /Metadata '.$xmpobj.' 0 R'; 12417 //$out .= ' /StructTreeRoot <<>>'; 12418 //$out .= ' /MarkInfo <<>>'; 12419 if (isset($this->l['a_meta_language'])) { 12420 $out .= ' /Lang '.$this->_textstring($this->l['a_meta_language'], $oid); 12421 } 12422 //$out .= ' /SpiderInfo <<>>'; 12423 // set OutputIntent to sRGB IEC61966-2.1 if required 12424 if ($this->pdfa_mode OR $this->force_srgb) { 12425 $out .= ' /OutputIntents [<<'; 12426 $out .= ' /Type /OutputIntent'; 12427 $out .= ' /S /GTS_PDFA1'; 12428 $out .= ' /OutputCondition '.$this->_textstring('sRGB IEC61966-2.1', $oid); 12429 $out .= ' /OutputConditionIdentifier '.$this->_textstring('sRGB IEC61966-2.1', $oid); 12430 $out .= ' /RegistryName '.$this->_textstring('http://www.color.org', $oid); 12431 $out .= ' /Info '.$this->_textstring('sRGB IEC61966-2.1', $oid); 12432 $out .= ' /DestOutputProfile '.$iccobj.' 0 R'; 12433 $out .= ' >>]'; 12434 } 12435 //$out .= ' /PieceInfo <<>>'; 12436 if (!empty($this->pdflayers)) { 12437 $lyrobjs = ''; 12438 $lyrobjs_print = ''; 12439 $lyrobjs_view = ''; 12440 foreach ($this->pdflayers as $layer) { 12441 $lyrobjs .= ' '.$layer['objid'].' 0 R'; 12442 if ($layer['print']) { 12443 $lyrobjs_print .= ' '.$layer['objid'].' 0 R'; 12444 } 12445 if ($layer['view']) { 12446 $lyrobjs_view .= ' '.$layer['objid'].' 0 R'; 12447 } 12448 } 12449 $out .= ' /OCProperties << /OCGs ['.$lyrobjs.']'; 12450 $out .= ' /D <<'; 12451 $out .= ' /Name '.$this->_textstring('Layers', $oid); 12452 $out .= ' /Creator '.$this->_textstring('TCPDF', $oid); 12453 $out .= ' /BaseState /ON'; 12454 $out .= ' /ON ['.$lyrobjs_print.']'; 12455 $out .= ' /OFF ['.$lyrobjs_view.']'; 12456 $out .= ' /Intent /View'; 12457 $out .= ' /AS ['; 12458 $out .= ' << /Event /Print /OCGs ['.$lyrobjs.'] /Category [/Print] >>'; 12459 $out .= ' << /Event /View /OCGs ['.$lyrobjs.'] /Category [/View] >>'; 12460 $out .= ' ]'; 12461 $out .= ' /Order ['.$lyrobjs.']'; 12462 $out .= ' /ListMode /AllPages'; 12463 //$out .= ' /RBGroups ['..']'; 12464 //$out .= ' /Locked ['..']'; 12465 $out .= ' >>'; 12466 $out .= ' >>'; 12467 } 12468 // AcroForm 12469 if (!empty($this->form_obj_id) OR ($this->sign AND isset($this->signature_data['cert_type']))) { 12470 $out .= ' /AcroForm <<'; 12471 $objrefs = ''; 12472 if ($this->sign AND isset($this->signature_data['cert_type'])) { 12473 // set reference for signature object 12474 $objrefs .= $this->sig_obj_id.' 0 R'; 12475 } 12476 if (!empty($this->empty_signature_appearance)) { 12477 foreach ($this->empty_signature_appearance as $esa) { 12478 // set reference for empty signature objects 12479 $objrefs .= ' '.$esa['objid'].' 0 R'; 12480 } 12481 } 12482 if (!empty($this->form_obj_id)) { 12483 foreach($this->form_obj_id as $objid) { 12484 $objrefs .= ' '.$objid.' 0 R'; 12485 } 12486 } 12487 $out .= ' /Fields ['.$objrefs.']'; 12488 // It's better to turn off this value and set the appearance stream for each annotation (/AP) to avoid conflicts with signature fields. 12489 $out .= ' /NeedAppearances false'; 12490 if ($this->sign AND isset($this->signature_data['cert_type'])) { 12491 if ($this->signature_data['cert_type'] > 0) { 12492 $out .= ' /SigFlags 3'; 12493 } else { 12494 $out .= ' /SigFlags 1'; 12495 } 12496 } 12497 //$out .= ' /CO '; 12498 if (isset($this->annotation_fonts) AND !empty($this->annotation_fonts)) { 12499 $out .= ' /DR <<'; 12500 $out .= ' /Font <<'; 12501 foreach ($this->annotation_fonts as $fontkey => $fontid) { 12502 $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R'; 12503 } 12504 $out .= ' >> >>'; 12505 } 12506 $font = $this->getFontBuffer('helvetica'); 12507 $out .= ' /DA (/F'.$font['i'].' 0 Tf 0 g)'; 12508 $out .= ' /Q '.(($this->rtl)?'2':'0'); 12509 //$out .= ' /XFA '; 12510 $out .= ' >>'; 12511 // signatures 12512 if ($this->sign AND isset($this->signature_data['cert_type'])) { 12513 if ($this->signature_data['cert_type'] > 0) { 12514 $out .= ' /Perms << /DocMDP '.($this->sig_obj_id + 1).' 0 R >>'; 12515 } else { 12516 $out .= ' /Perms << /UR3 '.($this->sig_obj_id + 1).' 0 R >>'; 12517 } 12518 } 12519 } 12520 //$out .= ' /Legal <<>>'; 12521 //$out .= ' /Requirements []'; 12522 //$out .= ' /Collection <<>>'; 12523 //$out .= ' /NeedsRendering true'; 12524 $out .= ' >>'; 12525 $out .= "\n".'endobj'; 12526 $this->_out($out); 12527 return $oid; 12528 } 12529 12537 protected function _putviewerpreferences() { 12538 $out = ' /ViewerPreferences <<'; 12539 if ($this->rtl) { 12540 $out .= ' /Direction /R2L'; 12541 } else { 12542 $out .= ' /Direction /L2R'; 12543 } 12544 if (isset($this->viewer_preferences['HideToolbar']) AND ($this->viewer_preferences['HideToolbar'])) { 12545 $out .= ' /HideToolbar true'; 12546 } 12547 if (isset($this->viewer_preferences['HideMenubar']) AND ($this->viewer_preferences['HideMenubar'])) { 12548 $out .= ' /HideMenubar true'; 12549 } 12550 if (isset($this->viewer_preferences['HideWindowUI']) AND ($this->viewer_preferences['HideWindowUI'])) { 12551 $out .= ' /HideWindowUI true'; 12552 } 12553 if (isset($this->viewer_preferences['FitWindow']) AND ($this->viewer_preferences['FitWindow'])) { 12554 $out .= ' /FitWindow true'; 12555 } 12556 if (isset($this->viewer_preferences['CenterWindow']) AND ($this->viewer_preferences['CenterWindow'])) { 12557 $out .= ' /CenterWindow true'; 12558 } 12559 if (isset($this->viewer_preferences['DisplayDocTitle']) AND ($this->viewer_preferences['DisplayDocTitle'])) { 12560 $out .= ' /DisplayDocTitle true'; 12561 } 12562 if (isset($this->viewer_preferences['NonFullScreenPageMode'])) { 12563 $out .= ' /NonFullScreenPageMode /'.$this->viewer_preferences['NonFullScreenPageMode']; 12564 } 12565 if (isset($this->viewer_preferences['ViewArea'])) { 12566 $out .= ' /ViewArea /'.$this->viewer_preferences['ViewArea']; 12567 } 12568 if (isset($this->viewer_preferences['ViewClip'])) { 12569 $out .= ' /ViewClip /'.$this->viewer_preferences['ViewClip']; 12570 } 12571 if (isset($this->viewer_preferences['PrintArea'])) { 12572 $out .= ' /PrintArea /'.$this->viewer_preferences['PrintArea']; 12573 } 12574 if (isset($this->viewer_preferences['PrintClip'])) { 12575 $out .= ' /PrintClip /'.$this->viewer_preferences['PrintClip']; 12576 } 12577 if (isset($this->viewer_preferences['PrintScaling'])) { 12578 $out .= ' /PrintScaling /'.$this->viewer_preferences['PrintScaling']; 12579 } 12580 if (isset($this->viewer_preferences['Duplex']) AND (!$this->empty_string($this->viewer_preferences['Duplex']))) { 12581 $out .= ' /Duplex /'.$this->viewer_preferences['Duplex']; 12582 } 12583 if (isset($this->viewer_preferences['PickTrayByPDFSize'])) { 12584 if ($this->viewer_preferences['PickTrayByPDFSize']) { 12585 $out .= ' /PickTrayByPDFSize true'; 12586 } else { 12587 $out .= ' /PickTrayByPDFSize false'; 12588 } 12589 } 12590 if (isset($this->viewer_preferences['PrintPageRange'])) { 12591 $PrintPageRangeNum = ''; 12592 foreach ($this->viewer_preferences['PrintPageRange'] as $k => $v) { 12593 $PrintPageRangeNum .= ' '.($v - 1).''; 12594 } 12595 $out .= ' /PrintPageRange ['.substr($PrintPageRangeNum,1).']'; 12596 } 12597 if (isset($this->viewer_preferences['NumCopies'])) { 12598 $out .= ' /NumCopies '.intval($this->viewer_preferences['NumCopies']); 12599 } 12600 $out .= ' >>'; 12601 return $out; 12602 } 12603 12608 protected function _putheader() { 12609 $this->_out('%PDF-'.$this->PDFVersion); 12610 $this->_out('%'.chr(0xe2).chr(0xe3).chr(0xcf).chr(0xd3)); 12611 } 12612 12617 protected function _enddoc() { 12618 $this->state = 1; 12619 $this->_putheader(); 12620 $this->_putpages(); 12621 $this->_putresources(); 12622 // empty signature fields 12623 if (!empty($this->empty_signature_appearance)) { 12624 foreach ($this->empty_signature_appearance as $key => $esa) { 12625 // widget annotation for empty signature 12626 $out = $this->_getobj($esa['objid'])."\n"; 12627 $out .= '<< /Type /Annot'; 12628 $out .= ' /Subtype /Widget'; 12629 $out .= ' /Rect ['.$esa['rect'].']'; 12630 $out .= ' /P '.$this->page_obj_id[($esa['page'])].' 0 R'; // link to signature appearance page 12631 $out .= ' /F 4'; 12632 $out .= ' /FT /Sig'; 12633 $signame = sprintf('Signature_%03d', ($key + 1)); 12634 $out .= ' /T '.$this->_textstring($signame, $esa['objid']); 12635 $out .= ' /Ff 0'; 12636 $out .= ' >>'; 12637 $out .= "\n".'endobj'; 12638 $this->_out($out); 12639 } 12640 } 12641 // Signature 12642 if ($this->sign AND isset($this->signature_data['cert_type'])) { 12643 // widget annotation for signature 12644 $out = $this->_getobj($this->sig_obj_id)."\n"; 12645 $out .= '<< /Type /Annot'; 12646 $out .= ' /Subtype /Widget'; 12647 $out .= ' /Rect ['.$this->signature_appearance['rect'].']'; 12648 $out .= ' /P '.$this->page_obj_id[($this->signature_appearance['page'])].' 0 R'; // link to signature appearance page 12649 $out .= ' /F 4'; 12650 $out .= ' /FT /Sig'; 12651 $out .= ' /T '.$this->_textstring('Signature_000', $this->sig_obj_id); 12652 $out .= ' /Ff 0'; 12653 $out .= ' /V '.($this->sig_obj_id + 1).' 0 R'; 12654 $out .= ' >>'; 12655 $out .= "\n".'endobj'; 12656 $this->_out($out); 12657 // signature 12658 $this->_putsignature(); 12659 } 12660 // Info 12661 $objid_info = $this->_putinfo(); 12662 // Catalog 12663 $objid_catalog = $this->_putcatalog(); 12664 // Cross-ref 12665 $o = $this->bufferlen; 12666 // XREF section 12667 $this->_out('xref'); 12668 $this->_out('0 '.($this->n + 1)); 12669 $this->_out('0000000000 65535 f '); 12670 for ($i=1; $i <= $this->n; ++$i) { 12671 if (!isset($this->offsets[$i]) AND ($i > 1)) { 12672 $this->offsets[$i] = $this->offsets[($i - 1)]; 12673 } 12674 $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i])); 12675 } 12676 // TRAILER 12677 $out = 'trailer'."\n"; 12678 $out .= '<<'; 12679 $out .= ' /Size '.($this->n + 1); 12680 $out .= ' /Root '.$objid_catalog.' 0 R'; 12681 $out .= ' /Info '.$objid_info.' 0 R'; 12682 if ($this->encrypted) { 12683 $out .= ' /Encrypt '.$this->encryptdata['objid'].' 0 R'; 12684 } 12685 $out .= ' /ID [ <'.$this->file_id.'> <'.$this->file_id.'> ]'; 12686 $out .= ' >>'; 12687 $this->_out($out); 12688 $this->_out('startxref'); 12689 $this->_out($o); 12690 $this->_out('%%EOF'); 12691 $this->state = 3; // end-of-doc 12692 if ($this->diskcache) { 12693 // remove temporary files used for images 12694 foreach ($this->imagekeys as $key) { 12695 // remove temporary files 12696 unlink($this->images[$key]); 12697 } 12698 foreach ($this->fontkeys as $key) { 12699 // remove temporary files 12700 unlink($this->fonts[$key]); 12701 } 12702 } 12703 } 12704 12712 protected function _beginpage($orientation='', $format='') { 12713 ++$this->page; 12714 $this->setPageBuffer($this->page, ''); 12715 // initialize array for graphics tranformation positions inside a page buffer 12716 $this->transfmrk[$this->page] = array(); 12717 $this->state = 2; 12718 if ($this->empty_string($orientation)) { 12719 if (isset($this->CurOrientation)) { 12720 $orientation = $this->CurOrientation; 12721 } elseif ($this->fwPt > $this->fhPt) { 12722 // landscape 12723 $orientation = 'L'; 12724 } else { 12725 // portrait 12726 $orientation = 'P'; 12727 } 12728 } 12729 if ($this->empty_string($format)) { 12730 $this->pagedim[$this->page] = $this->pagedim[($this->page - 1)]; 12731 $this->setPageOrientation($orientation); 12732 } else { 12733 $this->setPageFormat($format, $orientation); 12734 } 12735 if ($this->rtl) { 12736 $this->x = $this->w - $this->rMargin; 12737 } else { 12738 $this->x = $this->lMargin; 12739 } 12740 $this->y = $this->tMargin; 12741 if (isset($this->newpagegroup[$this->page])) { 12742 // start a new group 12743 $this->currpagegroup = $this->newpagegroup[$this->page]; 12744 $this->pagegroups[$this->currpagegroup] = 1; 12745 } elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) { 12746 ++$this->pagegroups[$this->currpagegroup]; 12747 } 12748 } 12749 12754 protected function _endpage() { 12755 $this->setVisibility('all'); 12756 $this->state = 1; 12757 } 12758 12764 protected function _newobj() { 12765 $this->_out($this->_getobj()); 12766 return $this->n; 12767 } 12768 12776 protected function _getobj($objid='') { 12777 if ($objid === '') { 12778 ++$this->n; 12779 $objid = $this->n; 12780 } 12781 $this->offsets[$objid] = $this->bufferlen; 12782 return $objid.' 0 obj'; 12783 } 12784 12792 protected function _dounderline($x, $y, $txt) { 12793 $w = $this->GetStringWidth($txt); 12794 return $this->_dounderlinew($x, $y, $w); 12795 } 12796 12805 protected function _dounderlinew($x, $y, $w) { 12806 $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt; 12807 return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew), $w * $this->k, $linew); 12808 } 12809 12817 protected function _dolinethrough($x, $y, $txt) { 12818 $w = $this->GetStringWidth($txt); 12819 return $this->_dolinethroughw($x, $y, $w); 12820 } 12821 12830 protected function _dolinethroughw($x, $y, $w) { 12831 $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt; 12832 return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew + ($this->FontSizePt / 3)), $w * $this->k, $linew); 12833 } 12834 12843 protected function _dooverline($x, $y, $txt) { 12844 $w = $this->GetStringWidth($txt); 12845 return $this->_dooverlinew($x, $y, $w); 12846 } 12847 12856 protected function _dooverlinew($x, $y, $w) { 12857 $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt; 12858 return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, (($this->h - $y + $this->FontAscent) * $this->k) - $linew, $w * $this->k, $linew); 12859 12860 } 12861 12868 protected function _freadint($f) { 12869 $a = unpack('Ni', fread($f, 4)); 12870 return $a['i']; 12871 } 12872 12879 protected function _escape($s) { 12880 // the chr(13) substitution fixes the Bugs item #1421290. 12881 return strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r')); 12882 } 12883 12891 protected function _datastring($s, $n=0) { 12892 if ($n == 0) { 12893 $n = $this->n; 12894 } 12895 $s = $this->_encrypt_data($n, $s); 12896 return '('. $this->_escape($s).')'; 12897 } 12898 12906 protected function _datestring($n=0) { 12907 return $this->_datastring('D:'.$this->doc_date, $n); 12908 } 12909 12917 protected function _textstring($s, $n=0) { 12918 if ($this->isunicode) { 12919 //Convert string to UTF-16BE 12920 $s = $this->UTF8ToUTF16BE($s, true); 12921 } 12922 return $this->_datastring($s, $n); 12923 } 12924 12933 protected function _escapetext($s) { 12934 if ($this->isunicode) { 12935 if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) { 12936 $s = $this->UTF8ToLatin1($s); 12937 } else { 12938 //Convert string to UTF-16BE and reverse RTL language 12939 $s = $this->utf8StrRev($s, false, $this->tmprtl); 12940 } 12941 } 12942 return $this->_escape($s); 12943 } 12944 12952 protected function _escapeXML($str) { 12953 $replaceTable = array("\0" => '', '&' => '&', '<' => '<', '>' => '>'); 12954 $str = strtr($str, $replaceTable); 12955 return $str; 12956 } 12957 12966 protected function _getrawstream($s, $n=0) { 12967 if ($n <= 0) { 12968 // default to current object 12969 $n = $this->n; 12970 } 12971 return $this->_encrypt_data($n, $s); 12972 } 12973 12981 protected function _getstream($s, $n=0) { 12982 return 'stream'."\n".$this->_getrawstream($s, $n)."\n".'endstream'; 12983 } 12984 12992 protected function _putstream($s, $n=0) { 12993 $this->_out($this->_getstream($s, $n)); 12994 } 12995 13001 protected function _out($s) { 13002 if ($this->state == 2) { 13003 if ($this->inxobj) { 13004 // we are inside an XObject template 13005 $this->xobjects[$this->xobjid]['outdata'] .= $s."\n"; 13006 } elseif ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) { 13007 // puts data before page footer 13008 $pagebuff = $this->getPageBuffer($this->page); 13009 $page = substr($pagebuff, 0, -$this->footerlen[$this->page]); 13010 $footer = substr($pagebuff, -$this->footerlen[$this->page]); 13011 $this->setPageBuffer($this->page, $page.$s."\n".$footer); 13012 // update footer position 13013 $this->footerpos[$this->page] += strlen($s."\n"); 13014 } else { 13015 $this->setPageBuffer($this->page, $s."\n", true); 13016 } 13017 } else { 13018 $this->setBuffer($s."\n"); 13019 } 13020 } 13021 13056 protected function UTF8StringToArray($str) { 13057 // build a unique string key 13058 $strkey = md5($str); 13059 if (isset($this->cache_UTF8StringToArray[$strkey])) { 13060 // return cached value 13061 $chrarray = $this->cache_UTF8StringToArray[$strkey]['s']; 13062 if (!isset($this->cache_UTF8StringToArray[$strkey]['f'][$this->CurrentFont['fontkey']])) { 13063 if ($this->isunicode) { 13064 foreach ($chrarray as $chr) { 13065 // store this char for font subsetting 13066 $this->CurrentFont['subsetchars'][$chr] = true; 13067 } 13068 // update font subsetchars 13069 $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']); 13070 } 13071 $this->cache_UTF8StringToArray[$strkey]['f'][$this->CurrentFont['fontkey']] = true; 13072 } 13073 return $chrarray; 13074 } 13075 // check cache size 13076 if ($this->cache_size_UTF8StringToArray >= $this->cache_maxsize_UTF8StringToArray) { 13077 // remove first element 13078 array_shift($this->cache_UTF8StringToArray); 13079 } 13080 // new cache array for selected string 13081 $this->cache_UTF8StringToArray[$strkey] = array('s' => array(), 'f' => array()); 13082 ++$this->cache_size_UTF8StringToArray; 13083 if (!$this->isunicode) { 13084 // split string into array of equivalent codes 13085 $strarr = array(); 13086 $strlen = strlen($str); 13087 for ($i=0; $i < $strlen; ++$i) { 13088 $strarr[] = ord($str{$i}); 13089 } 13090 // insert new value on cache 13091 $this->cache_UTF8StringToArray[$strkey]['s'] = $strarr; 13092 $this->cache_UTF8StringToArray[$strkey]['f'][$this->CurrentFont['fontkey']] = true; 13093 return $strarr; 13094 } 13095 $unichar = -1; // last unicode char 13096 $unicode = array(); // array containing unicode values 13097 $bytes = array(); // array containing single character byte sequences 13098 $numbytes = 1; // number of octetc needed to represent the UTF-8 character 13099 $str .= ''; // force $str to be a string 13100 $length = strlen($str); 13101 for ($i = 0; $i < $length; ++$i) { 13102 $char = ord($str{$i}); // get one string character at time 13103 if (count($bytes) == 0) { // get starting octect 13104 if ($char <= 0x7F) { 13105 $unichar = $char; // use the character "as is" because is ASCII 13106 $numbytes = 1; 13107 } elseif (($char >> 0x05) == 0x06) { // 2 bytes character (0x06 = 110 BIN) 13108 $bytes[] = ($char - 0xC0) << 0x06; 13109 $numbytes = 2; 13110 } elseif (($char >> 0x04) == 0x0E) { // 3 bytes character (0x0E = 1110 BIN) 13111 $bytes[] = ($char - 0xE0) << 0x0C; 13112 $numbytes = 3; 13113 } elseif (($char >> 0x03) == 0x1E) { // 4 bytes character (0x1E = 11110 BIN) 13114 $bytes[] = ($char - 0xF0) << 0x12; 13115 $numbytes = 4; 13116 } else { 13117 // use replacement character for other invalid sequences 13118 $unichar = 0xFFFD; 13119 $bytes = array(); 13120 $numbytes = 1; 13121 } 13122 } elseif (($char >> 0x06) == 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN 13123 $bytes[] = $char - 0x80; 13124 if (count($bytes) == $numbytes) { 13125 // compose UTF-8 bytes to a single unicode value 13126 $char = $bytes[0]; 13127 for ($j = 1; $j < $numbytes; ++$j) { 13128 $char += ($bytes[$j] << (($numbytes - $j - 1) * 0x06)); 13129 } 13130 if ((($char >= 0xD800) AND ($char <= 0xDFFF)) OR ($char >= 0x10FFFF)) { 13131 /* The definition of UTF-8 prohibits encoding character numbers between 13132 U+D800 and U+DFFF, which are reserved for use with the UTF-16 13133 encoding form (as surrogate pairs) and do not directly represent 13134 characters. */ 13135 $unichar = 0xFFFD; // use replacement character 13136 } else { 13137 $unichar = $char; // add char to array 13138 } 13139 // reset data for next char 13140 $bytes = array(); 13141 $numbytes = 1; 13142 } 13143 } else { 13144 // use replacement character for other invalid sequences 13145 $unichar = 0xFFFD; 13146 $bytes = array(); 13147 $numbytes = 1; 13148 } 13149 if ($unichar >= 0) { 13150 // insert unicode value into array 13151 $unicode[] = $unichar; 13152 // store this char for font subsetting 13153 $this->CurrentFont['subsetchars'][$unichar] = true; 13154 $unichar = -1; 13155 } 13156 } 13157 // update font subsetchars 13158 $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']); 13159 // insert new value on cache 13160 $this->cache_UTF8StringToArray[$strkey]['s'] = $unicode; 13161 $this->cache_UTF8StringToArray[$strkey]['f'][$this->CurrentFont['fontkey']] = true; 13162 return $unicode; 13163 } 13164 13175 protected function UTF8ToUTF16BE($str, $setbom=false) { 13176 if (!$this->isunicode) { 13177 return $str; // string is not in unicode 13178 } 13179 $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values 13180 return $this->arrUTF8ToUTF16BE($unicode, $setbom); 13181 } 13182 13191 protected function UTF8ToLatin1($str) { 13192 if (!$this->isunicode) { 13193 return $str; // string is not in unicode 13194 } 13195 $outstr = ''; // string to be returned 13196 $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values 13197 foreach ($unicode as $char) { 13198 if ($char < 256) { 13199 $outstr .= chr($char); 13200 } elseif (array_key_exists($char, $this->unicode->uni_utf8tolatin)) { 13201 // map from UTF-8 13202 $outstr .= chr($this->unicode->uni_utf8tolatin[$char]); 13203 } elseif ($char == 0xFFFD) { 13204 // skip 13205 } else { 13206 $outstr .= '?'; 13207 } 13208 } 13209 return $outstr; 13210 } 13211 13220 protected function UTF8ArrToLatin1($unicode) { 13221 if ((!$this->isunicode) OR $this->isUnicodeFont()) { 13222 return $unicode; 13223 } 13224 $outarr = array(); // array to be returned 13225 foreach ($unicode as $char) { 13226 if ($char < 256) { 13227 $outarr[] = $char; 13228 } elseif (array_key_exists($char, $this->unicode->uni_utf8tolatin)) { 13229 // map from UTF-8 13230 $outarr[] = $this->unicode->uni_utf8tolatin[$char]; 13231 } elseif ($char == 0xFFFD) { 13232 // skip 13233 } else { 13234 $outarr[] = 63; // '?' character 13235 } 13236 } 13237 return $outarr; 13238 } 13239 13278 protected function arrUTF8ToUTF16BE($unicode, $setbom=false) { 13279 $outstr = ''; // string to be returned 13280 if ($setbom) { 13281 $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM) 13282 } 13283 foreach ($unicode as $char) { 13284 if ($char == 0x200b) { 13285 // skip Unicode Character 'ZERO WIDTH SPACE' (DEC:8203, U+200B) 13286 } elseif ($char == 0xFFFD) { 13287 $outstr .= "\xFF\xFD"; // replacement character 13288 } elseif ($char < 0x10000) { 13289 $outstr .= chr($char >> 0x08); 13290 $outstr .= chr($char & 0xFF); 13291 } else { 13292 $char -= 0x10000; 13293 $w1 = 0xD800 | ($char >> 0x0a); 13294 $w2 = 0xDC00 | ($char & 0x3FF); 13295 $outstr .= chr($w1 >> 0x08); 13296 $outstr .= chr($w1 & 0xFF); 13297 $outstr .= chr($w2 >> 0x08); 13298 $outstr .= chr($w2 & 0xFF); 13299 } 13300 } 13301 return $outstr; 13302 } 13303 // ==================================================== 13304 13311 public function setHeaderFont($font) { 13312 $this->header_font = $font; 13313 } 13314 13321 public function getHeaderFont() { 13322 return $this->header_font; 13323 } 13324 13331 public function setFooterFont($font) { 13332 $this->footer_font = $font; 13333 } 13334 13341 public function getFooterFont() { 13342 return $this->footer_font; 13343 } 13344 13351 public function setLanguageArray($language) { 13352 $this->l = $language; 13353 if (isset($this->l['a_meta_dir'])) { 13354 $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false; 13355 } else { 13356 $this->rtl = false; 13357 } 13358 } 13359 13364 public function getPDFData() { 13365 if ($this->state < 3) { 13366 $this->Close(); 13367 } 13368 return $this->buffer; 13369 } 13370 13383 public function addHtmlLink($url, $name, $fill=false, $firstline=false, $color='', $style=-1, $firstblock=false) { 13384 if (!$this->empty_string($url) AND ($url{0} == '#')) { 13385 // convert url to internal link 13386 $lnkdata = explode(',', $url); 13387 if (isset($lnkdata[0])) { 13388 $page = intval(substr($lnkdata[0], 1)); 13389 if (empty($page) OR ($page <= 0)) { 13390 $page = $this->page; 13391 } 13392 if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) { 13393 $lnky = floatval($lnkdata[1]); 13394 } else { 13395 $lnky = 0; 13396 } 13397 $url = $this->AddLink(); 13398 $this->SetLink($url, $lnky, $page); 13399 } 13400 } 13401 // store current settings 13402 $prevcolor = $this->fgcolor; 13403 $prevstyle = $this->FontStyle; 13404 if (empty($color)) { 13405 $this->SetTextColorArray($this->htmlLinkColorArray); 13406 } else { 13407 $this->SetTextColorArray($color); 13408 } 13409 if ($style == -1) { 13410 $this->SetFont('', $this->FontStyle.$this->htmlLinkFontStyle); 13411 } else { 13412 $this->SetFont('', $this->FontStyle.$style); 13413 } 13414 $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline, $firstblock, 0); 13415 // restore settings 13416 $this->SetFont('', $prevstyle); 13417 $this->SetTextColorArray($prevcolor); 13418 return $ret; 13419 } 13420 13428 public function convertHTMLColorToDec($hcolor='#FFFFFF', $defcol=array(128,128,128)) { 13429 $color = preg_replace('/[\s]*/', '', $hcolor); // remove extra spaces 13430 $color = strtolower($color); 13431 if (($dotpos = strpos($color, '.')) !== false) { 13432 // remove class parent (i.e.: color.red) 13433 $color = substr($color, ($dotpos + 1)); 13434 } 13435 if (strlen($color) == 0) { 13436 return $defcol; 13437 } 13438 // RGB ARRAY 13439 if (substr($color, 0, 3) == 'rgb') { 13440 $codes = substr($color, 4); 13441 $codes = str_replace(')', '', $codes); 13442 $returncolor = explode(',', $codes); 13443 foreach ($returncolor as $key => $val) { 13444 if (strpos($val, '%') > 0) { 13445 // percentage 13446 $returncolor[$key] = (255 * intval($val) / 100); 13447 } else { 13448 $returncolor[$key] = intval($val); 13449 } 13450 // normalize value 13451 $returncolor[$key] = max(0, min(255, $returncolor[$key])); 13452 } 13453 return $returncolor; 13454 } 13455 // CMYK ARRAY 13456 if (substr($color, 0, 4) == 'cmyk') { 13457 $codes = substr($color, 5); 13458 $codes = str_replace(')', '', $codes); 13459 $returncolor = explode(',', $codes); 13460 foreach ($returncolor as $key => $val) { 13461 if (strpos($val, '%') !== false) { 13462 // percentage 13463 $returncolor[$key] = (100 * intval($val) / 100); 13464 } else { 13465 $returncolor[$key] = intval($val); 13466 } 13467 // normalize value 13468 $returncolor[$key] = max(0, min(100, $returncolor[$key])); 13469 } 13470 return $returncolor; 13471 } 13472 if ($color{0} != '#') { 13473 // COLOR NAME 13474 if (isset($this->webcolor[$color])) { 13475 // web color 13476 $color_code = $this->webcolor[$color]; 13477 } else { 13478 // spot color 13479 $returncolor = $this->getSpotColor($color); 13480 if ($returncolor === false) { 13481 $returncolor = $defcol; 13482 } 13483 return $returncolor; 13484 } 13485 } else { 13486 $color_code = substr($color, 1); 13487 } 13488 // HEXADECIMAL REPRESENTATION 13489 switch (strlen($color_code)) { 13490 case 3: { 13491 // 3-digit RGB hexadecimal representation 13492 $r = substr($color_code, 0, 1); 13493 $g = substr($color_code, 1, 1); 13494 $b = substr($color_code, 2, 1); 13495 $returncolor = array(); 13496 $returncolor['R'] = max(0, min(255, hexdec($r.$r))); 13497 $returncolor['G'] = max(0, min(255, hexdec($g.$g))); 13498 $returncolor['B'] = max(0, min(255, hexdec($b.$b))); 13499 break; 13500 } 13501 case 6: { 13502 // 6-digit RGB hexadecimal representation 13503 $returncolor = array(); 13504 $returncolor['R'] = max(0, min(255, hexdec(substr($color_code, 0, 2)))); 13505 $returncolor['G'] = max(0, min(255, hexdec(substr($color_code, 2, 2)))); 13506 $returncolor['B'] = max(0, min(255, hexdec(substr($color_code, 4, 2)))); 13507 break; 13508 } 13509 case 8: { 13510 // 8-digit CMYK hexadecimal representation 13511 $returncolor = array(); 13512 $returncolor['C'] = max(0, min(100, round(hexdec(substr($color_code, 0, 2)) / 2.55))); 13513 $returncolor['M'] = max(0, min(100, round(hexdec(substr($color_code, 2, 2)) / 2.55))); 13514 $returncolor['Y'] = max(0, min(100, round(hexdec(substr($color_code, 4, 2)) / 2.55))); 13515 $returncolor['K'] = max(0, min(100, round(hexdec(substr($color_code, 6, 2)) / 2.55))); 13516 break; 13517 } 13518 default: { 13519 $returncolor = $defcol; 13520 break; 13521 } 13522 } 13523 return $returncolor; 13524 } 13525 13533 public function pixelsToUnits($px) { 13534 return ($px / ($this->imgscale * $this->k)); 13535 } 13536 13544 public function unhtmlentities($text_to_convert) { 13545 return @html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding); 13546 } 13547 13548 // ENCRYPTION METHODS ---------------------------------- 13549 13558 protected function getRandomSeed($seed='') { 13559 $seed .= microtime(); 13560 if (function_exists('openssl_random_pseudo_bytes') AND (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN')) { 13561 // this is not used on windows systems because it is very slow for a know bug 13562 $seed .= openssl_random_pseudo_bytes(512); 13563 } else { 13564 for ($i = 0; $i < 23; ++$i) { 13565 $seed .= uniqid('', true); 13566 } 13567 } 13568 $seed .= uniqid('', true); 13569 $seed .= rand(); 13570 $seed .= getmypid(); 13571 $seed .= __FILE__; 13572 $seed .= $this->bufferlen; 13573 if (isset($_SERVER['REMOTE_ADDR'])) { 13574 $seed .= $_SERVER['REMOTE_ADDR']; 13575 } 13576 if (isset($_SERVER['HTTP_USER_AGENT'])) { 13577 $seed .= $_SERVER['HTTP_USER_AGENT']; 13578 } 13579 if (isset($_SERVER['HTTP_ACCEPT'])) { 13580 $seed .= $_SERVER['HTTP_ACCEPT']; 13581 } 13582 if (isset($_SERVER['HTTP_ACCEPT_ENCODING'])) { 13583 $seed .= $_SERVER['HTTP_ACCEPT_ENCODING']; 13584 } 13585 if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { 13586 $seed .= $_SERVER['HTTP_ACCEPT_LANGUAGE']; 13587 } 13588 if (isset($_SERVER['HTTP_ACCEPT_CHARSET'])) { 13589 $seed .= $_SERVER['HTTP_ACCEPT_CHARSET']; 13590 } 13591 $seed .= rand(); 13592 $seed .= uniqid('', true); 13593 $seed .= microtime(); 13594 return $seed; 13595 } 13596 13606 protected function _objectkey($n) { 13607 $objkey = $this->encryptdata['key'].pack('VXxx', $n); 13608 if ($this->encryptdata['mode'] == 2) { // AES-128 13609 // AES padding 13610 $objkey .= "\x73\x41\x6C\x54"; // sAlT 13611 } 13612 $objkey = substr($this->_md5_16($objkey), 0, (($this->encryptdata['Length'] / 8) + 5)); 13613 $objkey = substr($objkey, 0, 16); 13614 return $objkey; 13615 } 13616 13626 protected function _encrypt_data($n, $s) { 13627 if (!$this->encrypted) { 13628 return $s; 13629 } 13630 switch ($this->encryptdata['mode']) { 13631 case 0: // RC4-40 13632 case 1: { // RC4-128 13633 $s = $this->_RC4($this->_objectkey($n), $s); 13634 break; 13635 } 13636 case 2: { // AES-128 13637 $s = $this->_AES($this->_objectkey($n), $s); 13638 break; 13639 } 13640 case 3: { // AES-256 13641 $s = $this->_AES($this->encryptdata['key'], $s); 13642 break; 13643 } 13644 } 13645 return $s; 13646 } 13647 13654 protected function _putencryption() { 13655 if (!$this->encrypted) { 13656 return; 13657 } 13658 $this->encryptdata['objid'] = $this->_newobj(); 13659 $out = '<<'; 13660 if (!isset($this->encryptdata['Filter']) OR empty($this->encryptdata['Filter'])) { 13661 $this->encryptdata['Filter'] = 'Standard'; 13662 } 13663 $out .= ' /Filter /'.$this->encryptdata['Filter']; 13664 if (isset($this->encryptdata['SubFilter']) AND !empty($this->encryptdata['SubFilter'])) { 13665 $out .= ' /SubFilter /'.$this->encryptdata['SubFilter']; 13666 } 13667 if (!isset($this->encryptdata['V']) OR empty($this->encryptdata['V'])) { 13668 $this->encryptdata['V'] = 1; 13669 } 13670 // V is a code specifying the algorithm to be used in encrypting and decrypting the document 13671 $out .= ' /V '.$this->encryptdata['V']; 13672 if (isset($this->encryptdata['Length']) AND !empty($this->encryptdata['Length'])) { 13673 // The length of the encryption key, in bits. The value shall be a multiple of 8, in the range 40 to 256 13674 $out .= ' /Length '.$this->encryptdata['Length']; 13675 } else { 13676 $out .= ' /Length 40'; 13677 } 13678 if ($this->encryptdata['V'] >= 4) { 13679 if (!isset($this->encryptdata['StmF']) OR empty($this->encryptdata['StmF'])) { 13680 $this->encryptdata['StmF'] = 'Identity'; 13681 } 13682 if (!isset($this->encryptdata['StrF']) OR empty($this->encryptdata['StrF'])) { 13683 // The name of the crypt filter that shall be used when decrypting all strings in the document. 13684 $this->encryptdata['StrF'] = 'Identity'; 13685 } 13686 // A dictionary whose keys shall be crypt filter names and whose values shall be the corresponding crypt filter dictionaries. 13687 if (isset($this->encryptdata['CF']) AND !empty($this->encryptdata['CF'])) { 13688 $out .= ' /CF <<'; 13689 $out .= ' /'.$this->encryptdata['StmF'].' <<'; 13690 $out .= ' /Type /CryptFilter'; 13691 if (isset($this->encryptdata['CF']['CFM']) AND !empty($this->encryptdata['CF']['CFM'])) { 13692 // The method used 13693 $out .= ' /CFM /'.$this->encryptdata['CF']['CFM']; 13694 if ($this->encryptdata['pubkey']) { 13695 $out .= ' /Recipients ['; 13696 foreach ($this->encryptdata['Recipients'] as $rec) { 13697 $out .= ' <'.$rec.'>'; 13698 } 13699 $out .= ' ]'; 13700 if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) { 13701 $out .= ' /EncryptMetadata false'; 13702 } else { 13703 $out .= ' /EncryptMetadata true'; 13704 } 13705 } 13706 } else { 13707 $out .= ' /CFM /None'; 13708 } 13709 if (isset($this->encryptdata['CF']['AuthEvent']) AND !empty($this->encryptdata['CF']['AuthEvent'])) { 13710 // The event to be used to trigger the authorization that is required to access encryption keys used by this filter. 13711 $out .= ' /AuthEvent /'.$this->encryptdata['CF']['AuthEvent']; 13712 } else { 13713 $out .= ' /AuthEvent /DocOpen'; 13714 } 13715 if (isset($this->encryptdata['CF']['Length']) AND !empty($this->encryptdata['CF']['Length'])) { 13716 // The bit length of the encryption key. 13717 $out .= ' /Length '.$this->encryptdata['CF']['Length']; 13718 } 13719 $out .= ' >> >>'; 13720 } 13721 // The name of the crypt filter that shall be used by default when decrypting streams. 13722 $out .= ' /StmF /'.$this->encryptdata['StmF']; 13723 // The name of the crypt filter that shall be used when decrypting all strings in the document. 13724 $out .= ' /StrF /'.$this->encryptdata['StrF']; 13725 if (isset($this->encryptdata['EFF']) AND !empty($this->encryptdata['EFF'])) { 13726 // The name of the crypt filter that shall be used when encrypting embedded file streams that do not have their own crypt filter specifier. 13727 $out .= ' /EFF /'.$this->encryptdata['']; 13728 } 13729 } 13730 // Additional encryption dictionary entries for the standard security handler 13731 if ($this->encryptdata['pubkey']) { 13732 if (($this->encryptdata['V'] < 4) AND isset($this->encryptdata['Recipients']) AND !empty($this->encryptdata['Recipients'])) { 13733 $out .= ' /Recipients ['; 13734 foreach ($this->encryptdata['Recipients'] as $rec) { 13735 $out .= ' <'.$rec.'>'; 13736 } 13737 $out .= ' ]'; 13738 } 13739 } else { 13740 $out .= ' /R'; 13741 if ($this->encryptdata['V'] == 5) { // AES-256 13742 $out .= ' 5'; 13743 $out .= ' /OE ('.$this->_escape($this->encryptdata['OE']).')'; 13744 $out .= ' /UE ('.$this->_escape($this->encryptdata['UE']).')'; 13745 $out .= ' /Perms ('.$this->_escape($this->encryptdata['perms']).')'; 13746 } elseif ($this->encryptdata['V'] == 4) { // AES-128 13747 $out .= ' 4'; 13748 } elseif ($this->encryptdata['V'] < 2) { // RC-40 13749 $out .= ' 2'; 13750 } else { // RC-128 13751 $out .= ' 3'; 13752 } 13753 $out .= ' /O ('.$this->_escape($this->encryptdata['O']).')'; 13754 $out .= ' /U ('.$this->_escape($this->encryptdata['U']).')'; 13755 $out .= ' /P '.$this->encryptdata['P']; 13756 if (isset($this->encryptdata['EncryptMetadata']) AND (!$this->encryptdata['EncryptMetadata'])) { 13757 $out .= ' /EncryptMetadata false'; 13758 } else { 13759 $out .= ' /EncryptMetadata true'; 13760 } 13761 } 13762 $out .= ' >>'; 13763 $out .= "\n".'endobj'; 13764 $this->_out($out); 13765 } 13766 13777 protected function _RC4($key, $text) { 13778 if (function_exists('mcrypt_decrypt') AND ($out = @mcrypt_decrypt(MCRYPT_ARCFOUR, $key, $text, MCRYPT_MODE_STREAM, ''))) { 13779 // try to use mcrypt function if exist 13780 return $out; 13781 } 13782 if ($this->last_enc_key != $key) { 13783 $k = str_repeat($key, ((256 / strlen($key)) + 1)); 13784 $rc4 = range(0, 255); 13785 $j = 0; 13786 for ($i = 0; $i < 256; ++$i) { 13787 $t = $rc4[$i]; 13788 $j = ($j + $t + ord($k{$i})) % 256; 13789 $rc4[$i] = $rc4[$j]; 13790 $rc4[$j] = $t; 13791 } 13792 $this->last_enc_key = $key; 13793 $this->last_enc_key_c = $rc4; 13794 } else { 13795 $rc4 = $this->last_enc_key_c; 13796 } 13797 $len = strlen($text); 13798 $a = 0; 13799 $b = 0; 13800 $out = ''; 13801 for ($i = 0; $i < $len; ++$i) { 13802 $a = ($a + 1) % 256; 13803 $t = $rc4[$a]; 13804 $b = ($b + $t) % 256; 13805 $rc4[$a] = $rc4[$b]; 13806 $rc4[$b] = $t; 13807 $k = $rc4[($rc4[$a] + $rc4[$b]) % 256]; 13808 $out .= chr(ord($text{$i}) ^ $k); 13809 } 13810 return $out; 13811 } 13812 13823 protected function _AES($key, $text) { 13824 // padding (RFC 2898, PKCS #5: Password-Based Cryptography Specification Version 2.0) 13825 $padding = 16 - (strlen($text) % 16); 13826 $text .= str_repeat(chr($padding), $padding); 13827 $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_RAND); 13828 $text = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $text, MCRYPT_MODE_CBC, $iv); 13829 $text = $iv.$text; 13830 return $text; 13831 } 13832 13841 protected function _md5_16($str) { 13842 return pack('H*', md5($str)); 13843 } 13844 13852 protected function _Uvalue() { 13853 if ($this->encryptdata['mode'] == 0) { // RC4-40 13854 return $this->_RC4($this->encryptdata['key'], $this->enc_padding); 13855 } elseif ($this->encryptdata['mode'] < 3) { // RC4-128, AES-128 13856 $tmp = $this->_md5_16($this->enc_padding.$this->encryptdata['fileid']); 13857 $enc = $this->_RC4($this->encryptdata['key'], $tmp); 13858 $len = strlen($tmp); 13859 for ($i = 1; $i <= 19; ++$i) { 13860 $ek = ''; 13861 for ($j = 0; $j < $len; ++$j) { 13862 $ek .= chr(ord($this->encryptdata['key']{$j}) ^ $i); 13863 } 13864 $enc = $this->_RC4($ek, $enc); 13865 } 13866 $enc .= str_repeat("\x00", 16); 13867 return substr($enc, 0, 32); 13868 } elseif ($this->encryptdata['mode'] == 3) { // AES-256 13869 $seed = $this->_md5_16($this->getRandomSeed()); 13870 // User Validation Salt 13871 $this->encryptdata['UVS'] = substr($seed, 0, 8); 13872 // User Key Salt 13873 $this->encryptdata['UKS'] = substr($seed, 8, 16); 13874 return hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UVS'], true).$this->encryptdata['UVS'].$this->encryptdata['UKS']; 13875 } 13876 } 13877 13885 protected function _UEvalue() { 13886 $hashkey = hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UKS'], true); 13887 $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC)); 13888 return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $hashkey, $this->encryptdata['key'], MCRYPT_MODE_CBC, $iv); 13889 } 13890 13898 protected function _Ovalue() { 13899 if ($this->encryptdata['mode'] < 3) { // RC4-40, RC4-128, AES-128 13900 $tmp = $this->_md5_16($this->encryptdata['owner_password']); 13901 if ($this->encryptdata['mode'] > 0) { 13902 for ($i = 0; $i < 50; ++$i) { 13903 $tmp = $this->_md5_16($tmp); 13904 } 13905 } 13906 $owner_key = substr($tmp, 0, ($this->encryptdata['Length'] / 8)); 13907 $enc = $this->_RC4($owner_key, $this->encryptdata['user_password']); 13908 if ($this->encryptdata['mode'] > 0) { 13909 $len = strlen($owner_key); 13910 for ($i = 1; $i <= 19; ++$i) { 13911 $ek = ''; 13912 for ($j = 0; $j < $len; ++$j) { 13913 $ek .= chr(ord($owner_key{$j}) ^ $i); 13914 } 13915 $enc = $this->_RC4($ek, $enc); 13916 } 13917 } 13918 return $enc; 13919 } elseif ($this->encryptdata['mode'] == 3) { // AES-256 13920 $seed = $this->_md5_16($this->getRandomSeed()); 13921 // Owner Validation Salt 13922 $this->encryptdata['OVS'] = substr($seed, 0, 8); 13923 // Owner Key Salt 13924 $this->encryptdata['OKS'] = substr($seed, 8, 16); 13925 return hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OVS'].$this->encryptdata['U'], true).$this->encryptdata['OVS'].$this->encryptdata['OKS']; 13926 } 13927 } 13928 13936 protected function _OEvalue() { 13937 $hashkey = hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OKS'].$this->encryptdata['U'], true); 13938 $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC)); 13939 return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $hashkey, $this->encryptdata['key'], MCRYPT_MODE_CBC, $iv); 13940 } 13941 13950 protected function _fixAES256Password($password) { 13951 $psw = ''; // password to be returned 13952 $psw_array = $this->utf8Bidi($this->UTF8StringToArray($password), $password, $this->rtl); 13953 foreach ($psw_array as $c) { 13954 $psw .= $this->unichr($c); 13955 } 13956 return substr($psw, 0, 127); 13957 } 13958 13965 protected function _generateencryptionkey() { 13966 $keybytelen = ($this->encryptdata['Length'] / 8); 13967 if (!$this->encryptdata['pubkey']) { // standard mode 13968 if ($this->encryptdata['mode'] == 3) { // AES-256 13969 // generate 256 bit random key 13970 $this->encryptdata['key'] = substr(hash('sha256', $this->getRandomSeed(), true), 0, $keybytelen); 13971 // truncate passwords 13972 $this->encryptdata['user_password'] = $this->_fixAES256Password($this->encryptdata['user_password']); 13973 $this->encryptdata['owner_password'] = $this->_fixAES256Password($this->encryptdata['owner_password']); 13974 // Compute U value 13975 $this->encryptdata['U'] = $this->_Uvalue(); 13976 // Compute UE value 13977 $this->encryptdata['UE'] = $this->_UEvalue(); 13978 // Compute O value 13979 $this->encryptdata['O'] = $this->_Ovalue(); 13980 // Compute OE value 13981 $this->encryptdata['OE'] = $this->_OEvalue(); 13982 // Compute P value 13983 $this->encryptdata['P'] = $this->encryptdata['protection']; 13984 // Computing the encryption dictionary's Perms (permissions) value 13985 $perms = $this->getEncPermissionsString($this->encryptdata['protection']); // bytes 0-3 13986 $perms .= chr(255).chr(255).chr(255).chr(255); // bytes 4-7 13987 if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) { // byte 8 13988 $perms .= 'F'; 13989 } else { 13990 $perms .= 'T'; 13991 } 13992 $perms .= 'adb'; // bytes 9-11 13993 $perms .= 'nick'; // bytes 12-15 13994 $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB)); 13995 $this->encryptdata['perms'] = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $this->encryptdata['key'], $perms, MCRYPT_MODE_ECB, $iv); 13996 } else { // RC4-40, RC4-128, AES-128 13997 // Pad passwords 13998 $this->encryptdata['user_password'] = substr($this->encryptdata['user_password'].$this->enc_padding, 0, 32); 13999 $this->encryptdata['owner_password'] = substr($this->encryptdata['owner_password'].$this->enc_padding, 0, 32); 14000 // Compute O value 14001 $this->encryptdata['O'] = $this->_Ovalue(); 14002 // get default permissions (reverse byte order) 14003 $permissions = $this->getEncPermissionsString($this->encryptdata['protection']); 14004 // Compute encryption key 14005 $tmp = $this->_md5_16($this->encryptdata['user_password'].$this->encryptdata['O'].$permissions.$this->encryptdata['fileid']); 14006 if ($this->encryptdata['mode'] > 0) { 14007 for ($i = 0; $i < 50; ++$i) { 14008 $tmp = $this->_md5_16(substr($tmp, 0, $keybytelen)); 14009 } 14010 } 14011 $this->encryptdata['key'] = substr($tmp, 0, $keybytelen); 14012 // Compute U value 14013 $this->encryptdata['U'] = $this->_Uvalue(); 14014 // Compute P value 14015 $this->encryptdata['P'] = $this->encryptdata['protection']; 14016 } 14017 } else { // Public-Key mode 14018 // random 20-byte seed 14019 $seed = sha1($this->getRandomSeed(), true); 14020 $recipient_bytes = ''; 14021 foreach ($this->encryptdata['pubkeys'] as $pubkey) { 14022 // for each public certificate 14023 if (isset($pubkey['p'])) { 14024 $pkprotection = $this->getUserPermissionCode($pubkey['p'], $this->encryptdata['mode']); 14025 } else { 14026 $pkprotection = $this->encryptdata['protection']; 14027 } 14028 // get default permissions (reverse byte order) 14029 $pkpermissions = $this->getEncPermissionsString($pkprotection); 14030 // envelope data 14031 $envelope = $seed.$pkpermissions; 14032 // write the envelope data to a temporary file 14033 $tempkeyfile = tempnam(K_PATH_CACHE, 'tmpkey_'); 14034 $f = fopen($tempkeyfile, 'wb'); 14035 if (!$f) { 14036 $this->Error('Unable to create temporary key file: '.$tempkeyfile); 14037 } 14038 $envelope_length = strlen($envelope); 14039 fwrite($f, $envelope, $envelope_length); 14040 fclose($f); 14041 $tempencfile = tempnam(K_PATH_CACHE, 'tmpenc_'); 14042 if (!openssl_pkcs7_encrypt($tempkeyfile, $tempencfile, $pubkey['c'], array(), PKCS7_DETACHED | PKCS7_BINARY)) { 14043 $this->Error('Unable to encrypt the file: '.$tempkeyfile); 14044 } 14045 unlink($tempkeyfile); 14046 // read encryption signature 14047 $signature = file_get_contents($tempencfile, false, null, $envelope_length); 14048 unlink($tempencfile); 14049 // extract signature 14050 $signature = substr($signature, strpos($signature, 'Content-Disposition')); 14051 $tmparr = explode("\n\n", $signature); 14052 $signature = trim($tmparr[1]); 14053 unset($tmparr); 14054 // decode signature 14055 $signature = base64_decode($signature); 14056 // convert signature to hex 14057 $hexsignature = current(unpack('H*', $signature)); 14058 // store signature on recipients array 14059 $this->encryptdata['Recipients'][] = $hexsignature; 14060 // The bytes of each item in the Recipients array of PKCS#7 objects in the order in which they appear in the array 14061 $recipient_bytes .= $signature; 14062 } 14063 // calculate encryption key 14064 if ($this->encryptdata['mode'] == 3) { // AES-256 14065 $this->encryptdata['key'] = substr(hash('sha256', $seed.$recipient_bytes, true), 0, $keybytelen); 14066 } else { // RC4-40, RC4-128, AES-128 14067 $this->encryptdata['key'] = substr(sha1($seed.$recipient_bytes, true), 0, $keybytelen); 14068 } 14069 } 14070 } 14071 14080 protected function getUserPermissionCode($permissions, $mode=0) { 14081 $options = array( 14082 'owner' => 2, // bit 2 -- inverted logic: cleared by default 14083 'print' => 4, // bit 3 14084 'modify' => 8, // bit 4 14085 'copy' => 16, // bit 5 14086 'annot-forms' => 32, // bit 6 14087 'fill-forms' => 256, // bit 9 14088 'extract' => 512, // bit 10 14089 'assemble' => 1024,// bit 11 14090 'print-high' => 2048 // bit 12 14091 ); 14092 $protection = 2147422012; // 32 bit: (01111111 11111111 00001111 00111100) 14093 foreach ($permissions as $permission) { 14094 if (!isset($options[$permission])) { 14095 $this->Error('Incorrect permission: '.$permission); 14096 } 14097 if (($mode > 0) OR ($options[$permission] <= 32)) { 14098 // set only valid permissions 14099 if ($options[$permission] == 2) { 14100 // the logic for bit 2 is inverted (cleared by default) 14101 $protection += $options[$permission]; 14102 } else { 14103 $protection -= $options[$permission]; 14104 } 14105 } 14106 } 14107 return $protection; 14108 } 14109 14124 public function SetProtection($permissions=array('print', 'modify', 'copy', 'annot-forms', 'fill-forms', 'extract', 'assemble', 'print-high'), $user_pass='', $owner_pass=null, $mode=0, $pubkeys=null) { 14125 if ($this->pdfa_mode) { 14126 // encryption is not allowed in PDF/A mode 14127 return; 14128 } 14129 $this->encryptdata['protection'] = $this->getUserPermissionCode($permissions, $mode); 14130 if (($pubkeys !== null) AND (is_array($pubkeys))) { 14131 // public-key mode 14132 $this->encryptdata['pubkeys'] = $pubkeys; 14133 if ($mode == 0) { 14134 // public-Key Security requires at least 128 bit 14135 $mode = 1; 14136 } 14137 if (!function_exists('openssl_pkcs7_encrypt')) { 14138 $this->Error('Public-Key Security requires openssl library.'); 14139 } 14140 // Set Public-Key filter (availabe are: Entrust.PPKEF, Adobe.PPKLite, Adobe.PubSec) 14141 $this->encryptdata['pubkey'] = true; 14142 $this->encryptdata['Filter'] = 'Adobe.PubSec'; 14143 $this->encryptdata['StmF'] = 'DefaultCryptFilter'; 14144 $this->encryptdata['StrF'] = 'DefaultCryptFilter'; 14145 } else { 14146 // standard mode (password mode) 14147 $this->encryptdata['pubkey'] = false; 14148 $this->encryptdata['Filter'] = 'Standard'; 14149 $this->encryptdata['StmF'] = 'StdCF'; 14150 $this->encryptdata['StrF'] = 'StdCF'; 14151 } 14152 if ($mode > 1) { // AES 14153 if (!extension_loaded('mcrypt')) { 14154 $this->Error('AES encryption requires mcrypt library (http://www.php.net/manual/en/mcrypt.requirements.php).'); 14155 } 14156 if (mcrypt_get_cipher_name(MCRYPT_RIJNDAEL_128) === false) { 14157 $this->Error('AES encryption requires MCRYPT_RIJNDAEL_128 cypher.'); 14158 } 14159 if (($mode == 3) AND !function_exists('hash')) { 14160 // the Hash extension requires no external libraries and is enabled by default as of PHP 5.1.2. 14161 $this->Error('AES 256 encryption requires HASH Message Digest Framework (http://www.php.net/manual/en/book.hash.php).'); 14162 } 14163 } 14164 if ($owner_pass === null) { 14165 $owner_pass = md5($this->getRandomSeed()); 14166 } 14167 $this->encryptdata['user_password'] = $user_pass; 14168 $this->encryptdata['owner_password'] = $owner_pass; 14169 $this->encryptdata['mode'] = $mode; 14170 switch ($mode) { 14171 case 0: { // RC4 40 bit 14172 $this->encryptdata['V'] = 1; 14173 $this->encryptdata['Length'] = 40; 14174 $this->encryptdata['CF']['CFM'] = 'V2'; 14175 break; 14176 } 14177 case 1: { // RC4 128 bit 14178 $this->encryptdata['V'] = 2; 14179 $this->encryptdata['Length'] = 128; 14180 $this->encryptdata['CF']['CFM'] = 'V2'; 14181 if ($this->encryptdata['pubkey']) { 14182 $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s4'; 14183 $this->encryptdata['Recipients'] = array(); 14184 } 14185 break; 14186 } 14187 case 2: { // AES 128 bit 14188 $this->encryptdata['V'] = 4; 14189 $this->encryptdata['Length'] = 128; 14190 $this->encryptdata['CF']['CFM'] = 'AESV2'; 14191 $this->encryptdata['CF']['Length'] = 128; 14192 if ($this->encryptdata['pubkey']) { 14193 $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5'; 14194 $this->encryptdata['Recipients'] = array(); 14195 } 14196 break; 14197 } 14198 case 3: { // AES 256 bit 14199 $this->encryptdata['V'] = 5; 14200 $this->encryptdata['Length'] = 256; 14201 $this->encryptdata['CF']['CFM'] = 'AESV3'; 14202 $this->encryptdata['CF']['Length'] = 256; 14203 if ($this->encryptdata['pubkey']) { 14204 $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5'; 14205 $this->encryptdata['Recipients'] = array(); 14206 } 14207 break; 14208 } 14209 } 14210 $this->encrypted = true; 14211 $this->encryptdata['fileid'] = $this->convertHexStringToString($this->file_id); 14212 $this->_generateencryptionkey(); 14213 } 14214 14223 protected function convertHexStringToString($bs) { 14224 $string = ''; // string to be returned 14225 $bslength = strlen($bs); 14226 if (($bslength % 2) != 0) { 14227 // padding 14228 $bs .= '0'; 14229 ++$bslength; 14230 } 14231 for ($i = 0; $i < $bslength; $i += 2) { 14232 $string .= chr(hexdec($bs{$i}.$bs{($i + 1)})); 14233 } 14234 return $string; 14235 } 14236 14245 protected function convertStringToHexString($s) { 14246 $bs = ''; 14247 $chars = preg_split('//', $s, -1, PREG_SPLIT_NO_EMPTY); 14248 foreach ($chars as $c) { 14249 $bs .= sprintf('%02s', dechex(ord($c))); 14250 } 14251 return $bs; 14252 } 14253 14262 protected function getEncPermissionsString($protection) { 14263 $binprot = sprintf('%032b', $protection); 14264 $str = chr(bindec(substr($binprot, 24, 8))); 14265 $str .= chr(bindec(substr($binprot, 16, 8))); 14266 $str .= chr(bindec(substr($binprot, 8, 8))); 14267 $str .= chr(bindec(substr($binprot, 0, 8))); 14268 return $str; 14269 } 14270 14271 // END OF ENCRYPTION FUNCTIONS ------------------------- 14272 14273 // START TRANSFORMATIONS SECTION ----------------------- 14274 14283 public function StartTransform() { 14284 $this->_out('q'); 14285 if ($this->inxobj) { 14286 // we are inside an XObject template 14287 $this->xobjects[$this->xobjid]['transfmrk'][] = strlen($this->xobjects[$this->xobjid]['outdata']); 14288 } else { 14289 $this->transfmrk[$this->page][] = $this->pagelen[$this->page]; 14290 } 14291 ++$this->transfmatrix_key; 14292 $this->transfmatrix[$this->transfmatrix_key] = array(); 14293 } 14294 14303 public function StopTransform() { 14304 $this->_out('Q'); 14305 if (isset($this->transfmatrix[$this->transfmatrix_key])) { 14306 array_pop($this->transfmatrix[$this->transfmatrix_key]); 14307 --$this->transfmatrix_key; 14308 } 14309 if ($this->inxobj) { 14310 // we are inside an XObject template 14311 array_pop($this->xobjects[$this->xobjid]['transfmrk']); 14312 } else { 14313 array_pop($this->transfmrk[$this->page]); 14314 } 14315 } 14325 public function ScaleX($s_x, $x='', $y='') { 14326 $this->Scale($s_x, 100, $x, $y); 14327 } 14328 14338 public function ScaleY($s_y, $x='', $y='') { 14339 $this->Scale(100, $s_y, $x, $y); 14340 } 14341 14351 public function ScaleXY($s, $x='', $y='') { 14352 $this->Scale($s, $s, $x, $y); 14353 } 14354 14365 public function Scale($s_x, $s_y, $x='', $y='') { 14366 if ($x === '') { 14367 $x = $this->x; 14368 } 14369 if ($y === '') { 14370 $y = $this->y; 14371 } 14372 if (($s_x == 0) OR ($s_y == 0)) { 14373 $this->Error('Please do not use values equal to zero for scaling'); 14374 } 14375 $y = ($this->h - $y) * $this->k; 14376 $x *= $this->k; 14377 //calculate elements of transformation matrix 14378 $s_x /= 100; 14379 $s_y /= 100; 14380 $tm = array(); 14381 $tm[0] = $s_x; 14382 $tm[1] = 0; 14383 $tm[2] = 0; 14384 $tm[3] = $s_y; 14385 $tm[4] = $x * (1 - $s_x); 14386 $tm[5] = $y * (1 - $s_y); 14387 //scale the coordinate system 14388 $this->Transform($tm); 14389 } 14390 14398 public function MirrorH($x='') { 14399 $this->Scale(-100, 100, $x); 14400 } 14401 14409 public function MirrorV($y='') { 14410 $this->Scale(100, -100, '', $y); 14411 } 14412 14421 public function MirrorP($x='',$y='') { 14422 $this->Scale(-100, -100, $x, $y); 14423 } 14424 14434 public function MirrorL($angle=0, $x='',$y='') { 14435 $this->Scale(-100, 100, $x, $y); 14436 $this->Rotate(-2*($angle-90), $x, $y); 14437 } 14438 14446 public function TranslateX($t_x) { 14447 $this->Translate($t_x, 0); 14448 } 14449 14457 public function TranslateY($t_y) { 14458 $this->Translate(0, $t_y); 14459 } 14460 14469 public function Translate($t_x, $t_y) { 14470 //calculate elements of transformation matrix 14471 $tm = array(); 14472 $tm[0] = 1; 14473 $tm[1] = 0; 14474 $tm[2] = 0; 14475 $tm[3] = 1; 14476 $tm[4] = $t_x * $this->k; 14477 $tm[5] = -$t_y * $this->k; 14478 //translate the coordinate system 14479 $this->Transform($tm); 14480 } 14481 14491 public function Rotate($angle, $x='', $y='') { 14492 if ($x === '') { 14493 $x = $this->x; 14494 } 14495 if ($y === '') { 14496 $y = $this->y; 14497 } 14498 $y = ($this->h - $y) * $this->k; 14499 $x *= $this->k; 14500 //calculate elements of transformation matrix 14501 $tm = array(); 14502 $tm[0] = cos(deg2rad($angle)); 14503 $tm[1] = sin(deg2rad($angle)); 14504 $tm[2] = -$tm[1]; 14505 $tm[3] = $tm[0]; 14506 $tm[4] = $x + ($tm[1] * $y) - ($tm[0] * $x); 14507 $tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x); 14508 //rotate the coordinate system around ($x,$y) 14509 $this->Transform($tm); 14510 } 14511 14521 public function SkewX($angle_x, $x='', $y='') { 14522 $this->Skew($angle_x, 0, $x, $y); 14523 } 14524 14534 public function SkewY($angle_y, $x='', $y='') { 14535 $this->Skew(0, $angle_y, $x, $y); 14536 } 14537 14548 public function Skew($angle_x, $angle_y, $x='', $y='') { 14549 if ($x === '') { 14550 $x = $this->x; 14551 } 14552 if ($y === '') { 14553 $y = $this->y; 14554 } 14555 if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) { 14556 $this->Error('Please use values between -90 and +90 degrees for Skewing.'); 14557 } 14558 $x *= $this->k; 14559 $y = ($this->h - $y) * $this->k; 14560 //calculate elements of transformation matrix 14561 $tm = array(); 14562 $tm[0] = 1; 14563 $tm[1] = tan(deg2rad($angle_y)); 14564 $tm[2] = tan(deg2rad($angle_x)); 14565 $tm[3] = 1; 14566 $tm[4] = -$tm[2] * $y; 14567 $tm[5] = -$tm[1] * $x; 14568 //skew the coordinate system 14569 $this->Transform($tm); 14570 } 14571 14579 protected function Transform($tm) { 14580 $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5])); 14581 // add tranformation matrix 14582 $this->transfmatrix[$this->transfmatrix_key][] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]); 14583 // update transformation mark 14584 if ($this->inxobj) { 14585 // we are inside an XObject template 14586 if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) { 14587 $key = key($this->xobjects[$this->xobjid]['transfmrk']); 14588 $this->xobjects[$this->xobjid]['transfmrk'][$key] = strlen($this->xobjects[$this->xobjid]['outdata']); 14589 } 14590 } elseif (end($this->transfmrk[$this->page]) !== false) { 14591 $key = key($this->transfmrk[$this->page]); 14592 $this->transfmrk[$this->page][$key] = $this->pagelen[$this->page]; 14593 } 14594 } 14595 14596 // END TRANSFORMATIONS SECTION ------------------------- 14597 14598 // START GRAPHIC FUNCTIONS SECTION --------------------- 14599 // The following section is based on the code provided by David Hernandez Sanz 14600 14608 public function SetLineWidth($width) { 14609 //Set line width 14610 $this->LineWidth = $width; 14611 $this->linestyleWidth = sprintf('%.2F w', ($width * $this->k)); 14612 if ($this->page > 0) { 14613 $this->_out($this->linestyleWidth); 14614 } 14615 } 14616 14624 public function GetLineWidth() { 14625 return $this->LineWidth; 14626 } 14627 14651 public function SetLineStyle($style, $ret=false) { 14652 $s = ''; // string to be returned 14653 if (!is_array($style)) { 14654 return; 14655 } 14656 if (isset($style['width'])) { 14657 $this->LineWidth = $style['width']; 14658 $this->linestyleWidth = sprintf('%.2F w', ($style['width'] * $this->k)); 14659 $s .= $this->linestyleWidth.' '; 14660 } 14661 if (isset($style['cap'])) { 14662 $ca = array('butt' => 0, 'round'=> 1, 'square' => 2); 14663 if (isset($ca[$style['cap']])) { 14664 $this->linestyleCap = $ca[$style['cap']].' J'; 14665 $s .= $this->linestyleCap.' '; 14666 } 14667 } 14668 if (isset($style['join'])) { 14669 $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2); 14670 if (isset($ja[$style['join']])) { 14671 $this->linestyleJoin = $ja[$style['join']].' j'; 14672 $s .= $this->linestyleJoin.' '; 14673 } 14674 } 14675 if (isset($style['dash'])) { 14676 $dash_string = ''; 14677 if ($style['dash']) { 14678 if (preg_match('/^.+,/', $style['dash']) > 0) { 14679 $tab = explode(',', $style['dash']); 14680 } else { 14681 $tab = array($style['dash']); 14682 } 14683 $dash_string = ''; 14684 foreach ($tab as $i => $v) { 14685 if ($i) { 14686 $dash_string .= ' '; 14687 } 14688 $dash_string .= sprintf('%.2F', $v); 14689 } 14690 } 14691 if (!isset($style['phase']) OR !$style['dash']) { 14692 $style['phase'] = 0; 14693 } 14694 $this->linestyleDash = sprintf('[%s] %.2F d', $dash_string, $style['phase']); 14695 $s .= $this->linestyleDash.' '; 14696 } 14697 if (isset($style['color'])) { 14698 $s .= $this->SetDrawColorArray($style['color'], true).' '; 14699 } 14700 if (!$ret) { 14701 $this->_out($s); 14702 } 14703 return $s; 14704 } 14705 14713 protected function _outPoint($x, $y) { 14714 $this->_out(sprintf('%.2F %.2F m', $x * $this->k, ($this->h - $y) * $this->k)); 14715 } 14716 14725 protected function _outLine($x, $y) { 14726 $this->_out(sprintf('%.2F %.2F l', $x * $this->k, ($this->h - $y) * $this->k)); 14727 } 14728 14739 protected function _outRect($x, $y, $w, $h, $op) { 14740 $this->_out(sprintf('%.2F %.2F %.2F %.2F re %s', $x * $this->k, ($this->h - $y) * $this->k, $w * $this->k, -$h * $this->k, $op)); 14741 } 14742 14755 protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) { 14756 $this->_out(sprintf('%.2F %.2F %.2F %.2F %.2F %.2F c', $x1 * $this->k, ($this->h - $y1) * $this->k, $x2 * $this->k, ($this->h - $y2) * $this->k, $x3 * $this->k, ($this->h - $y3) * $this->k)); 14757 } 14758 14769 protected function _outCurveV($x2, $y2, $x3, $y3) { 14770 $this->_out(sprintf('%.2F %.2F %.2F %.2F v', $x2 * $this->k, ($this->h - $y2) * $this->k, $x3 * $this->k, ($this->h - $y3) * $this->k)); 14771 } 14772 14783 protected function _outCurveY($x1, $y1, $x3, $y3) { 14784 $this->_out(sprintf('%.2F %.2F %.2F %.2F y', $x1 * $this->k, ($this->h - $y1) * $this->k, $x3 * $this->k, ($this->h - $y3) * $this->k)); 14785 } 14786 14798 public function Line($x1, $y1, $x2, $y2, $style=array()) { 14799 if (is_array($style)) { 14800 $this->SetLineStyle($style); 14801 } 14802 $this->_outPoint($x1, $y1); 14803 $this->_outLine($x2, $y2); 14804 $this->_out('S'); 14805 } 14806 14826 public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) { 14827 if (!(false === strpos($style, 'F')) AND !empty($fill_color)) { 14828 $this->SetFillColorArray($fill_color); 14829 } 14830 $op = $this->getPathPaintOperator($style); 14831 if ((!$border_style) OR (isset($border_style['all']))) { 14832 if (isset($border_style['all']) AND $border_style['all']) { 14833 $this->SetLineStyle($border_style['all']); 14834 $border_style = array(); 14835 } 14836 } 14837 $this->_outRect($x, $y, $w, $h, $op); 14838 if ($border_style) { 14839 $border_style2 = array(); 14840 foreach ($border_style as $line => $value) { 14841 $length = strlen($line); 14842 for ($i = 0; $i < $length; ++$i) { 14843 $border_style2[$line[$i]] = $value; 14844 } 14845 } 14846 $border_style = $border_style2; 14847 if (isset($border_style['L']) AND $border_style['L']) { 14848 $this->Line($x, $y, $x, $y + $h, $border_style['L']); 14849 } 14850 if (isset($border_style['T']) AND $border_style['T']) { 14851 $this->Line($x, $y, $x + $w, $y, $border_style['T']); 14852 } 14853 if (isset($border_style['R']) AND $border_style['R']) { 14854 $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style['R']); 14855 } 14856 if (isset($border_style['B']) AND $border_style['B']) { 14857 $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style['B']); 14858 } 14859 } 14860 } 14861 14881 public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) { 14882 if (!(false === strpos($style, 'F')) AND isset($fill_color)) { 14883 $this->SetFillColorArray($fill_color); 14884 } 14885 $op = $this->getPathPaintOperator($style); 14886 if ($line_style) { 14887 $this->SetLineStyle($line_style); 14888 } 14889 $this->_outPoint($x0, $y0); 14890 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3); 14891 $this->_out($op); 14892 } 14893 14908 public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) { 14909 if (!(false === strpos($style, 'F')) AND isset($fill_color)) { 14910 $this->SetFillColorArray($fill_color); 14911 } 14912 $op = $this->getPathPaintOperator($style); 14913 if ($op == 'f') { 14914 $line_style = array(); 14915 } 14916 if ($line_style) { 14917 $this->SetLineStyle($line_style); 14918 } 14919 $this->_outPoint($x0, $y0); 14920 foreach ($segments as $segment) { 14921 list($x1, $y1, $x2, $y2, $x3, $y3) = $segment; 14922 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3); 14923 } 14924 $this->_out($op); 14925 } 14926 14945 public function Ellipse($x0, $y0, $rx, $ry='', $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) { 14946 if ($this->empty_string($ry) OR ($ry == 0)) { 14947 $ry = $rx; 14948 } 14949 if (!(false === strpos($style, 'F')) AND isset($fill_color)) { 14950 $this->SetFillColorArray($fill_color); 14951 } 14952 $op = $this->getPathPaintOperator($style); 14953 if ($op == 'f') { 14954 $line_style = array(); 14955 } 14956 if ($line_style) { 14957 $this->SetLineStyle($line_style); 14958 } 14959 $this->_outellipticalarc($x0, $y0, $rx, $ry, $angle, $astart, $afinish, false, $nc, true, true, false); 14960 $this->_out($op); 14961 } 14962 14983 protected function _outellipticalarc($xc, $yc, $rx, $ry, $xang=0, $angs=0, $angf=360, $pie=false, $nc=2, $startpoint=true, $ccw=true, $svg=false) { 14984 $k = $this->k; 14985 if ($nc < 2) { 14986 $nc = 2; 14987 } 14988 $xmin = 2147483647; 14989 $ymin = 2147483647; 14990 $xmax = 0; 14991 $ymax = 0; 14992 if ($pie) { 14993 // center of the arc 14994 $this->_outPoint($xc, $yc); 14995 } 14996 $xang = deg2rad((float) $xang); 14997 $angs = deg2rad((float) $angs); 14998 $angf = deg2rad((float) $angf); 14999 if ($svg) { 15000 $as = $angs; 15001 $af = $angf; 15002 } else { 15003 $as = atan2((sin($angs) / $ry), (cos($angs) / $rx)); 15004 $af = atan2((sin($angf) / $ry), (cos($angf) / $rx)); 15005 } 15006 if ($as < 0) { 15007 $as += (2 * M_PI); 15008 } 15009 if ($af < 0) { 15010 $af += (2 * M_PI); 15011 } 15012 if ($ccw AND ($as > $af)) { 15013 // reverse rotation 15014 $as -= (2 * M_PI); 15015 } elseif (!$ccw AND ($as < $af)) { 15016 // reverse rotation 15017 $af -= (2 * M_PI); 15018 } 15019 $total_angle = ($af - $as); 15020 if ($nc < 2) { 15021 $nc = 2; 15022 } 15023 // total arcs to draw 15024 $nc *= (2 * abs($total_angle) / M_PI); 15025 $nc = round($nc) + 1; 15026 // angle of each arc 15027 $arcang = ($total_angle / $nc); 15028 // center point in PDF coordinates 15029 $x0 = $xc; 15030 $y0 = ($this->h - $yc); 15031 // starting angle 15032 $ang = $as; 15033 $alpha = sin($arcang) * ((sqrt(4 + (3 * pow(tan(($arcang) / 2), 2))) - 1) / 3); 15034 $cos_xang = cos($xang); 15035 $sin_xang = sin($xang); 15036 $cos_ang = cos($ang); 15037 $sin_ang = sin($ang); 15038 // first arc point 15039 $px1 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang); 15040 $py1 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang); 15041 // first Bezier control point 15042 $qx1 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang))); 15043 $qy1 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang))); 15044 if ($pie) { 15045 // line from center to arc starting point 15046 $this->_outLine($px1, $this->h - $py1); 15047 } elseif ($startpoint) { 15048 // arc starting point 15049 $this->_outPoint($px1, $this->h - $py1); 15050 } 15051 // draw arcs 15052 for ($i = 1; $i <= $nc; ++$i) { 15053 // starting angle 15054 $ang = $as + ($i * $arcang); 15055 if ($i == $nc) { 15056 $ang = $af; 15057 } 15058 $cos_ang = cos($ang); 15059 $sin_ang = sin($ang); 15060 // second arc point 15061 $px2 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang); 15062 $py2 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang); 15063 // second Bezier control point 15064 $qx2 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang))); 15065 $qy2 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang))); 15066 // draw arc 15067 $cx1 = ($px1 + $qx1); 15068 $cy1 = ($this->h - ($py1 + $qy1)); 15069 $cx2 = ($px2 - $qx2); 15070 $cy2 = ($this->h - ($py2 - $qy2)); 15071 $cx3 = $px2; 15072 $cy3 = ($this->h - $py2); 15073 $this->_outCurve($cx1, $cy1, $cx2, $cy2, $cx3, $cy3); 15074 // get bounding box coordinates 15075 $xmin = min($xmin, $cx1, $cx2, $cx3); 15076 $ymin = min($ymin, $cy1, $cy2, $cy3); 15077 $xmax = max($xmax, $cx1, $cx2, $cx3); 15078 $ymax = max($ymax, $cy1, $cy2, $cy3); 15079 // move to next point 15080 $px1 = $px2; 15081 $py1 = $py2; 15082 $qx1 = $qx2; 15083 $qy1 = $qy2; 15084 } 15085 if ($pie) { 15086 $this->_outLine($xc, $yc); 15087 // get bounding box coordinates 15088 $xmin = min($xmin, $xc); 15089 $ymin = min($ymin, $yc); 15090 $xmax = max($xmax, $xc); 15091 $ymax = max($ymax, $yc); 15092 } 15093 return array($xmin, $ymin, $xmax, $ymax); 15094 } 15095 15111 public function Circle($x0, $y0, $r, $angstr=0, $angend=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) { 15112 $this->Ellipse($x0, $y0, $r, $r, 0, $angstr, $angend, $style, $line_style, $fill_color, $nc); 15113 } 15114 15129 public function PolyLine($p, $style='', $line_style=array(), $fill_color=array()) { 15130 $this->Polygon($p, $style, $line_style, $fill_color, false); 15131 } 15132 15148 public function Polygon($p, $style='', $line_style=array(), $fill_color=array(), $closed=true) { 15149 $nc = count($p); // number of coordinates 15150 $np = $nc / 2; // number of points 15151 if ($closed) { 15152 // close polygon by adding the first 2 points at the end (one line) 15153 for ($i = 0; $i < 4; ++$i) { 15154 $p[$nc + $i] = $p[$i]; 15155 } 15156 // copy style for the last added line 15157 if (isset($line_style[0])) { 15158 $line_style[$np] = $line_style[0]; 15159 } 15160 $nc += 4; 15161 } 15162 if (!(false === strpos($style, 'F')) AND isset($fill_color)) { 15163 $this->SetFillColorArray($fill_color); 15164 } 15165 $op = $this->getPathPaintOperator($style); 15166 if ($op == 'f') { 15167 $line_style = array(); 15168 } 15169 $draw = true; 15170 if ($line_style) { 15171 if (isset($line_style['all'])) { 15172 $this->SetLineStyle($line_style['all']); 15173 } else { 15174 $draw = false; 15175 if ($op == 'B') { 15176 // draw fill 15177 $op = 'f'; 15178 $this->_outPoint($p[0], $p[1]); 15179 for ($i = 2; $i < $nc; $i = $i + 2) { 15180 $this->_outLine($p[$i], $p[$i + 1]); 15181 } 15182 $this->_out($op); 15183 } 15184 // draw outline 15185 $this->_outPoint($p[0], $p[1]); 15186 for ($i = 2; $i < $nc; $i = $i + 2) { 15187 $line_num = ($i / 2) - 1; 15188 if (isset($line_style[$line_num])) { 15189 if ($line_style[$line_num] != 0) { 15190 if (is_array($line_style[$line_num])) { 15191 $this->_out('S'); 15192 $this->SetLineStyle($line_style[$line_num]); 15193 $this->_outPoint($p[$i - 2], $p[$i - 1]); 15194 $this->_outLine($p[$i], $p[$i + 1]); 15195 $this->_out('S'); 15196 $this->_outPoint($p[$i], $p[$i + 1]); 15197 } else { 15198 $this->_outLine($p[$i], $p[$i + 1]); 15199 } 15200 } 15201 } else { 15202 $this->_outLine($p[$i], $p[$i + 1]); 15203 } 15204 } 15205 $this->_out($op); 15206 } 15207 } 15208 if ($draw) { 15209 $this->_outPoint($p[0], $p[1]); 15210 for ($i = 2; $i < $nc; $i = $i + 2) { 15211 $this->_outLine($p[$i], $p[$i + 1]); 15212 } 15213 $this->_out($op); 15214 } 15215 } 15216 15246 public function RegularPolygon($x0, $y0, $r, $ns, $angle=0, $draw_circle=false, $style='', $line_style=array(), $fill_color=array(), $circle_style='', $circle_outLine_style=array(), $circle_fill_color=array()) { 15247 if (3 > $ns) { 15248 $ns = 3; 15249 } 15250 if ($draw_circle) { 15251 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color); 15252 } 15253 $p = array(); 15254 for ($i = 0; $i < $ns; ++$i) { 15255 $a = $angle + ($i * 360 / $ns); 15256 $a_rad = deg2rad((float) $a); 15257 $p[] = $x0 + ($r * sin($a_rad)); 15258 $p[] = $y0 + ($r * cos($a_rad)); 15259 } 15260 $this->Polygon($p, $style, $line_style, $fill_color); 15261 } 15262 15294 public function StarPolygon($x0, $y0, $r, $nv, $ng, $angle=0, $draw_circle=false, $style='', $line_style=array(), $fill_color=array(), $circle_style='', $circle_outLine_style=array(), $circle_fill_color=array()) { 15295 if ($nv < 2) { 15296 $nv = 2; 15297 } 15298 if ($draw_circle) { 15299 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color); 15300 } 15301 $p2 = array(); 15302 $visited = array(); 15303 for ($i = 0; $i < $nv; ++$i) { 15304 $a = $angle + ($i * 360 / $nv); 15305 $a_rad = deg2rad((float) $a); 15306 $p2[] = $x0 + ($r * sin($a_rad)); 15307 $p2[] = $y0 + ($r * cos($a_rad)); 15308 $visited[] = false; 15309 } 15310 $p = array(); 15311 $i = 0; 15312 do { 15313 $p[] = $p2[$i * 2]; 15314 $p[] = $p2[($i * 2) + 1]; 15315 $visited[$i] = true; 15316 $i += $ng; 15317 $i %= $nv; 15318 } while (!$visited[$i]); 15319 $this->Polygon($p, $style, $line_style, $fill_color); 15320 } 15321 15336 public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) { 15337 $this->RoundedRectXY($x, $y, $w, $h, $r, $r, $round_corner, $style, $border_style, $fill_color); 15338 } 15339 15355 public function RoundedRectXY($x, $y, $w, $h, $rx, $ry, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) { 15356 if (($round_corner == '0000') OR (($rx == $ry) AND ($rx == 0))) { 15357 // Not rounded 15358 $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color); 15359 return; 15360 } 15361 // Rounded 15362 if (!(false === strpos($style, 'F')) AND isset($fill_color)) { 15363 $this->SetFillColorArray($fill_color); 15364 } 15365 $op = $this->getPathPaintOperator($style); 15366 if ($op == 'f') { 15367 $border_style = array(); 15368 } 15369 if ($border_style) { 15370 $this->SetLineStyle($border_style); 15371 } 15372 $MyArc = 4 / 3 * (sqrt(2) - 1); 15373 $this->_outPoint($x + $rx, $y); 15374 $xc = $x + $w - $rx; 15375 $yc = $y + $ry; 15376 $this->_outLine($xc, $y); 15377 if ($round_corner[0]) { 15378 $this->_outCurve($xc + ($rx * $MyArc), $yc - $ry, $xc + $rx, $yc - ($ry * $MyArc), $xc + $rx, $yc); 15379 } else { 15380 $this->_outLine($x + $w, $y); 15381 } 15382 $xc = $x + $w - $rx; 15383 $yc = $y + $h - $ry; 15384 $this->_outLine($x + $w, $yc); 15385 if ($round_corner[1]) { 15386 $this->_outCurve($xc + $rx, $yc + ($ry * $MyArc), $xc + ($rx * $MyArc), $yc + $ry, $xc, $yc + $ry); 15387 } else { 15388 $this->_outLine($x + $w, $y + $h); 15389 } 15390 $xc = $x + $rx; 15391 $yc = $y + $h - $ry; 15392 $this->_outLine($xc, $y + $h); 15393 if ($round_corner[2]) { 15394 $this->_outCurve($xc - ($rx * $MyArc), $yc + $ry, $xc - $rx, $yc + ($ry * $MyArc), $xc - $rx, $yc); 15395 } else { 15396 $this->_outLine($x, $y + $h); 15397 } 15398 $xc = $x + $rx; 15399 $yc = $y + $ry; 15400 $this->_outLine($x, $yc); 15401 if ($round_corner[3]) { 15402 $this->_outCurve($xc - $rx, $yc - ($ry * $MyArc), $xc - ($rx * $MyArc), $yc - $ry, $xc, $yc - $ry); 15403 } else { 15404 $this->_outLine($x, $y); 15405 $this->_outLine($x + $rx, $y); 15406 } 15407 $this->_out($op); 15408 } 15409 15422 public function Arrow($x0, $y0, $x1, $y1, $head_style=0, $arm_size=5, $arm_angle=15) { 15423 // getting arrow direction angle 15424 // 0 deg angle is when both arms go along X axis. angle grows clockwise. 15425 $dir_angle = atan2(($y0 - $y1), ($x0 - $x1)); 15426 if ($dir_angle < 0) { 15427 $dir_angle += (2 * M_PI); 15428 } 15429 $arm_angle = deg2rad($arm_angle); 15430 $sx1 = $x1; 15431 $sy1 = $y1; 15432 if ($head_style > 0) { 15433 // calculate the stopping point for the arrow shaft 15434 $sx1 = $x1 + (($arm_size - $this->LineWidth) * cos($dir_angle)); 15435 $sy1 = $y1 + (($arm_size - $this->LineWidth) * sin($dir_angle)); 15436 } 15437 // main arrow line / shaft 15438 $this->Line($x0, $y0, $sx1, $sy1); 15439 // left arrowhead arm tip 15440 $x2L = $x1 + ($arm_size * cos($dir_angle + $arm_angle)); 15441 $y2L = $y1 + ($arm_size * sin($dir_angle + $arm_angle)); 15442 // right arrowhead arm tip 15443 $x2R = $x1 + ($arm_size * cos($dir_angle - $arm_angle)); 15444 $y2R = $y1 + ($arm_size * sin($dir_angle - $arm_angle)); 15445 $mode = 'D'; 15446 $style = array(); 15447 switch ($head_style) { 15448 case 0: { 15449 // draw only arrowhead arms 15450 $mode = 'D'; 15451 $style = array(1, 1, 0); 15452 break; 15453 } 15454 case 1: { 15455 // draw closed arrowhead, but no fill 15456 $mode = 'D'; 15457 break; 15458 } 15459 case 2: { 15460 // closed and filled arrowhead 15461 $mode = 'DF'; 15462 break; 15463 } 15464 case 3: { 15465 // filled arrowhead 15466 $mode = 'F'; 15467 break; 15468 } 15469 } 15470 $this->Polygon(array($x2L, $y2L, $x1, $y1, $x2R, $y2R), $mode, $style, array()); 15471 } 15472 15473 // END GRAPHIC FUNCTIONS SECTION ----------------------- 15474 15475 // BIDIRECTIONAL TEXT SECTION -------------------------- 15476 15487 protected function utf8StrRev($str, $setbom=false, $forcertl=false) { 15488 return $this->utf8StrArrRev($this->UTF8StringToArray($str), $str, $setbom, $forcertl); 15489 } 15490 15502 protected function utf8StrArrRev($arr, $str='', $setbom=false, $forcertl=false) { 15503 return $this->arrUTF8ToUTF16BE($this->utf8Bidi($arr, $str, $forcertl), $setbom); 15504 } 15505 15516 protected function utf8Bidi($ta, $str='', $forcertl=false) { 15517 // paragraph embedding level 15518 $pel = 0; 15519 // max level 15520 $maxlevel = 0; 15521 if ($this->empty_string($str)) { 15522 // create string from array 15523 $str = $this->UTF8ArrSubString($ta); 15524 } 15525 // check if string contains arabic text 15526 if (preg_match($this->unicode->uni_RE_PATTERN_ARABIC, $str)) { 15527 $arabic = true; 15528 } else { 15529 $arabic = false; 15530 } 15531 // check if string contains RTL text 15532 if (!($forcertl OR $arabic OR preg_match($this->unicode->uni_RE_PATTERN_RTL, $str))) { 15533 return $ta; 15534 } 15535 15536 // get number of chars 15537 $numchars = count($ta); 15538 15539 if ($forcertl == 'R') { 15540 $pel = 1; 15541 } elseif ($forcertl == 'L') { 15542 $pel = 0; 15543 } else { 15544 // P2. In each paragraph, find the first character of type L, AL, or R. 15545 // P3. If a character is found in P2 and it is of type AL or R, then set the paragraph embedding level to one; otherwise, set it to zero. 15546 for ($i=0; $i < $numchars; ++$i) { 15547 $type = $this->unicode->uni_type[$ta[$i]]; 15548 if ($type == 'L') { 15549 $pel = 0; 15550 break; 15551 } elseif (($type == 'AL') OR ($type == 'R')) { 15552 $pel = 1; 15553 break; 15554 } 15555 } 15556 } 15557 15558 // Current Embedding Level 15559 $cel = $pel; 15560 // directional override status 15561 $dos = 'N'; 15562 $remember = array(); 15563 // start-of-level-run 15564 $sor = $pel % 2 ? 'R' : 'L'; 15565 $eor = $sor; 15566 15567 // Array of characters data 15568 $chardata = Array(); 15569 15570 // X1. Begin by setting the current embedding level to the paragraph embedding level. Set the directional override status to neutral. Process each character iteratively, applying rules X2 through X9. Only embedding levels from 0 to 61 are valid in this phase. 15571 // In the resolution of levels in rules I1 and I2, the maximum embedding level of 62 can be reached. 15572 for ($i=0; $i < $numchars; ++$i) { 15573 if ($ta[$i] == $this->unicode->uni_RLE) { 15574 // X2. With each RLE, compute the least greater odd embedding level. 15575 // a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to neutral. 15576 // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status. 15577 $next_level = $cel + ($cel % 2) + 1; 15578 if ($next_level < 62) { 15579 $remember[] = array('num' => $this->unicode->uni_RLE, 'cel' => $cel, 'dos' => $dos); 15580 $cel = $next_level; 15581 $dos = 'N'; 15582 $sor = $eor; 15583 $eor = $cel % 2 ? 'R' : 'L'; 15584 } 15585 } elseif ($ta[$i] == $this->unicode->uni_LRE) { 15586 // X3. With each LRE, compute the least greater even embedding level. 15587 // a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to neutral. 15588 // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status. 15589 $next_level = $cel + 2 - ($cel % 2); 15590 if ( $next_level < 62 ) { 15591 $remember[] = array('num' => $this->unicode->uni_LRE, 'cel' => $cel, 'dos' => $dos); 15592 $cel = $next_level; 15593 $dos = 'N'; 15594 $sor = $eor; 15595 $eor = $cel % 2 ? 'R' : 'L'; 15596 } 15597 } elseif ($ta[$i] == $this->unicode->uni_RLO) { 15598 // X4. With each RLO, compute the least greater odd embedding level. 15599 // a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to right-to-left. 15600 // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status. 15601 $next_level = $cel + ($cel % 2) + 1; 15602 if ($next_level < 62) { 15603 $remember[] = array('num' => $this->unicode->uni_RLO, 'cel' => $cel, 'dos' => $dos); 15604 $cel = $next_level; 15605 $dos = 'R'; 15606 $sor = $eor; 15607 $eor = $cel % 2 ? 'R' : 'L'; 15608 } 15609 } elseif ($ta[$i] == $this->unicode->uni_LRO) { 15610 // X5. With each LRO, compute the least greater even embedding level. 15611 // a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to left-to-right. 15612 // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status. 15613 $next_level = $cel + 2 - ($cel % 2); 15614 if ( $next_level < 62 ) { 15615 $remember[] = array('num' => $this->unicode->uni_LRO, 'cel' => $cel, 'dos' => $dos); 15616 $cel = $next_level; 15617 $dos = 'L'; 15618 $sor = $eor; 15619 $eor = $cel % 2 ? 'R' : 'L'; 15620 } 15621 } elseif ($ta[$i] == $this->unicode->uni_PDF) { 15622 // X7. With each PDF, determine the matching embedding or override code. If there was a valid matching code, restore (pop) the last remembered (pushed) embedding level and directional override. 15623 if (count($remember)) { 15624 $last = count($remember ) - 1; 15625 if (($remember[$last]['num'] == $this->unicode->uni_RLE) OR 15626 ($remember[$last]['num'] == $this->unicode->uni_LRE) OR 15627 ($remember[$last]['num'] == $this->unicode->uni_RLO) OR 15628 ($remember[$last]['num'] == $this->unicode->uni_LRO)) { 15629 $match = array_pop($remember); 15630 $cel = $match['cel']; 15631 $dos = $match['dos']; 15632 $sor = $eor; 15633 $eor = ($cel > $match['cel'] ? $cel : $match['cel']) % 2 ? 'R' : 'L'; 15634 } 15635 } 15636 } elseif (($ta[$i] != $this->unicode->uni_RLE) AND 15637 ($ta[$i] != $this->unicode->uni_LRE) AND 15638 ($ta[$i] != $this->unicode->uni_RLO) AND 15639 ($ta[$i] != $this->unicode->uni_LRO) AND 15640 ($ta[$i] != $this->unicode->uni_PDF)) { 15641 // X6. For all types besides RLE, LRE, RLO, LRO, and PDF: 15642 // a. Set the level of the current character to the current embedding level. 15643 // b. Whenever the directional override status is not neutral, reset the current character type to the directional override status. 15644 if ($dos != 'N') { 15645 $chardir = $dos; 15646 } else { 15647 if (isset($this->unicode->uni_type[$ta[$i]])) { 15648 $chardir = $this->unicode->uni_type[$ta[$i]]; 15649 } else { 15650 $chardir = 'L'; 15651 } 15652 } 15653 // stores string characters and other information 15654 $chardata[] = array('char' => $ta[$i], 'level' => $cel, 'type' => $chardir, 'sor' => $sor, 'eor' => $eor); 15655 } 15656 } // end for each char 15657 15658 // X8. All explicit directional embeddings and overrides are completely terminated at the end of each paragraph. Paragraph separators are not included in the embedding. 15659 // X9. Remove all RLE, LRE, RLO, LRO, PDF, and BN codes. 15660 // X10. The remaining rules are applied to each run of characters at the same level. For each run, determine the start-of-level-run (sor) and end-of-level-run (eor) type, either L or R. This depends on the higher of the two levels on either side of the boundary (at the start or end of the paragraph, the level of the 'other' run is the base embedding level). If the higher level is odd, the type is R; otherwise, it is L. 15661 15662 // 3.3.3 Resolving Weak Types 15663 // Weak types are now resolved one level run at a time. At level run boundaries where the type of the character on the other side of the boundary is required, the type assigned to sor or eor is used. 15664 // Nonspacing marks are now resolved based on the previous characters. 15665 $numchars = count($chardata); 15666 15667 // W1. Examine each nonspacing mark (NSM) in the level run, and change the type of the NSM to the type of the previous character. If the NSM is at the start of the level run, it will get the type of sor. 15668 $prevlevel = -1; // track level changes 15669 $levcount = 0; // counts consecutive chars at the same level 15670 for ($i=0; $i < $numchars; ++$i) { 15671 if ($chardata[$i]['type'] == 'NSM') { 15672 if ($levcount) { 15673 $chardata[$i]['type'] = $chardata[$i]['sor']; 15674 } elseif ($i > 0) { 15675 $chardata[$i]['type'] = $chardata[($i-1)]['type']; 15676 } 15677 } 15678 if ($chardata[$i]['level'] != $prevlevel) { 15679 $levcount = 0; 15680 } else { 15681 ++$levcount; 15682 } 15683 $prevlevel = $chardata[$i]['level']; 15684 } 15685 15686 // W2. Search backward from each instance of a European number until the first strong type (R, L, AL, or sor) is found. If an AL is found, change the type of the European number to Arabic number. 15687 $prevlevel = -1; 15688 $levcount = 0; 15689 for ($i=0; $i < $numchars; ++$i) { 15690 if ($chardata[$i]['char'] == 'EN') { 15691 for ($j=$levcount; $j >= 0; $j--) { 15692 if ($chardata[$j]['type'] == 'AL') { 15693 $chardata[$i]['type'] = 'AN'; 15694 } elseif (($chardata[$j]['type'] == 'L') OR ($chardata[$j]['type'] == 'R')) { 15695 break; 15696 } 15697 } 15698 } 15699 if ($chardata[$i]['level'] != $prevlevel) { 15700 $levcount = 0; 15701 } else { 15702 ++$levcount; 15703 } 15704 $prevlevel = $chardata[$i]['level']; 15705 } 15706 15707 // W3. Change all ALs to R. 15708 for ($i=0; $i < $numchars; ++$i) { 15709 if ($chardata[$i]['type'] == 'AL') { 15710 $chardata[$i]['type'] = 'R'; 15711 } 15712 } 15713 15714 // W4. A single European separator between two European numbers changes to a European number. A single common separator between two numbers of the same type changes to that type. 15715 $prevlevel = -1; 15716 $levcount = 0; 15717 for ($i=0; $i < $numchars; ++$i) { 15718 if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) { 15719 if (($chardata[$i]['type'] == 'ES') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) { 15720 $chardata[$i]['type'] = 'EN'; 15721 } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) { 15722 $chardata[$i]['type'] = 'EN'; 15723 } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'AN') AND ($chardata[($i+1)]['type'] == 'AN')) { 15724 $chardata[$i]['type'] = 'AN'; 15725 } 15726 } 15727 if ($chardata[$i]['level'] != $prevlevel) { 15728 $levcount = 0; 15729 } else { 15730 ++$levcount; 15731 } 15732 $prevlevel = $chardata[$i]['level']; 15733 } 15734 15735 // W5. A sequence of European terminators adjacent to European numbers changes to all European numbers. 15736 $prevlevel = -1; 15737 $levcount = 0; 15738 for ($i=0; $i < $numchars; ++$i) { 15739 if ($chardata[$i]['type'] == 'ET') { 15740 if (($levcount > 0) AND ($chardata[($i-1)]['type'] == 'EN')) { 15741 $chardata[$i]['type'] = 'EN'; 15742 } else { 15743 $j = $i+1; 15744 while (($j < $numchars) AND ($chardata[$j]['level'] == $prevlevel)) { 15745 if ($chardata[$j]['type'] == 'EN') { 15746 $chardata[$i]['type'] = 'EN'; 15747 break; 15748 } elseif ($chardata[$j]['type'] != 'ET') { 15749 break; 15750 } 15751 ++$j; 15752 } 15753 } 15754 } 15755 if ($chardata[$i]['level'] != $prevlevel) { 15756 $levcount = 0; 15757 } else { 15758 ++$levcount; 15759 } 15760 $prevlevel = $chardata[$i]['level']; 15761 } 15762 15763 // W6. Otherwise, separators and terminators change to Other Neutral. 15764 $prevlevel = -1; 15765 $levcount = 0; 15766 for ($i=0; $i < $numchars; ++$i) { 15767 if (($chardata[$i]['type'] == 'ET') OR ($chardata[$i]['type'] == 'ES') OR ($chardata[$i]['type'] == 'CS')) { 15768 $chardata[$i]['type'] = 'ON'; 15769 } 15770 if ($chardata[$i]['level'] != $prevlevel) { 15771 $levcount = 0; 15772 } else { 15773 ++$levcount; 15774 } 15775 $prevlevel = $chardata[$i]['level']; 15776 } 15777 15778 //W7. Search backward from each instance of a European number until the first strong type (R, L, or sor) is found. If an L is found, then change the type of the European number to L. 15779 $prevlevel = -1; 15780 $levcount = 0; 15781 for ($i=0; $i < $numchars; ++$i) { 15782 if ($chardata[$i]['char'] == 'EN') { 15783 for ($j=$levcount; $j >= 0; $j--) { 15784 if ($chardata[$j]['type'] == 'L') { 15785 $chardata[$i]['type'] = 'L'; 15786 } elseif ($chardata[$j]['type'] == 'R') { 15787 break; 15788 } 15789 } 15790 } 15791 if ($chardata[$i]['level'] != $prevlevel) { 15792 $levcount = 0; 15793 } else { 15794 ++$levcount; 15795 } 15796 $prevlevel = $chardata[$i]['level']; 15797 } 15798 15799 // N1. A sequence of neutrals takes the direction of the surrounding strong text if the text on both sides has the same direction. European and Arabic numbers act as if they were R in terms of their influence on neutrals. Start-of-level-run (sor) and end-of-level-run (eor) are used at level run boundaries. 15800 $prevlevel = -1; 15801 $levcount = 0; 15802 for ($i=0; $i < $numchars; ++$i) { 15803 if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) { 15804 if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) { 15805 $chardata[$i]['type'] = 'L'; 15806 } elseif (($chardata[$i]['type'] == 'N') AND 15807 (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND 15808 (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) { 15809 $chardata[$i]['type'] = 'R'; 15810 } elseif ($chardata[$i]['type'] == 'N') { 15811 // N2. Any remaining neutrals take the embedding direction 15812 $chardata[$i]['type'] = $chardata[$i]['sor']; 15813 } 15814 } elseif (($levcount == 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) { 15815 // first char 15816 if (($chardata[$i]['type'] == 'N') AND ($chardata[$i]['sor'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) { 15817 $chardata[$i]['type'] = 'L'; 15818 } elseif (($chardata[$i]['type'] == 'N') AND 15819 (($chardata[$i]['sor'] == 'R') OR ($chardata[$i]['sor'] == 'EN') OR ($chardata[$i]['sor'] == 'AN')) AND 15820 (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) { 15821 $chardata[$i]['type'] = 'R'; 15822 } elseif ($chardata[$i]['type'] == 'N') { 15823 // N2. Any remaining neutrals take the embedding direction 15824 $chardata[$i]['type'] = $chardata[$i]['sor']; 15825 } 15826 } elseif (($levcount > 0) AND ((($i+1) == $numchars) OR (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] != $prevlevel))) { 15827 //last char 15828 if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[$i]['eor'] == 'L')) { 15829 $chardata[$i]['type'] = 'L'; 15830 } elseif (($chardata[$i]['type'] == 'N') AND 15831 (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND 15832 (($chardata[$i]['eor'] == 'R') OR ($chardata[$i]['eor'] == 'EN') OR ($chardata[$i]['eor'] == 'AN'))) { 15833 $chardata[$i]['type'] = 'R'; 15834 } elseif ($chardata[$i]['type'] == 'N') { 15835 // N2. Any remaining neutrals take the embedding direction 15836 $chardata[$i]['type'] = $chardata[$i]['sor']; 15837 } 15838 } elseif ($chardata[$i]['type'] == 'N') { 15839 // N2. Any remaining neutrals take the embedding direction 15840 $chardata[$i]['type'] = $chardata[$i]['sor']; 15841 } 15842 if ($chardata[$i]['level'] != $prevlevel) { 15843 $levcount = 0; 15844 } else { 15845 ++$levcount; 15846 } 15847 $prevlevel = $chardata[$i]['level']; 15848 } 15849 15850 // I1. For all characters with an even (left-to-right) embedding direction, those of type R go up one level and those of type AN or EN go up two levels. 15851 // I2. For all characters with an odd (right-to-left) embedding direction, those of type L, EN or AN go up one level. 15852 for ($i=0; $i < $numchars; ++$i) { 15853 $odd = $chardata[$i]['level'] % 2; 15854 if ($odd) { 15855 if (($chardata[$i]['type'] == 'L') OR ($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')) { 15856 $chardata[$i]['level'] += 1; 15857 } 15858 } else { 15859 if ($chardata[$i]['type'] == 'R') { 15860 $chardata[$i]['level'] += 1; 15861 } elseif (($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')) { 15862 $chardata[$i]['level'] += 2; 15863 } 15864 } 15865 $maxlevel = max($chardata[$i]['level'],$maxlevel); 15866 } 15867 15868 // L1. On each line, reset the embedding level of the following characters to the paragraph embedding level: 15869 // 1. Segment separators, 15870 // 2. Paragraph separators, 15871 // 3. Any sequence of whitespace characters preceding a segment separator or paragraph separator, and 15872 // 4. Any sequence of white space characters at the end of the line. 15873 for ($i=0; $i < $numchars; ++$i) { 15874 if (($chardata[$i]['type'] == 'B') OR ($chardata[$i]['type'] == 'S')) { 15875 $chardata[$i]['level'] = $pel; 15876 } elseif ($chardata[$i]['type'] == 'WS') { 15877 $j = $i+1; 15878 while ($j < $numchars) { 15879 if ((($chardata[$j]['type'] == 'B') OR ($chardata[$j]['type'] == 'S')) OR 15880 (($j == ($numchars-1)) AND ($chardata[$j]['type'] == 'WS'))) { 15881 $chardata[$i]['level'] = $pel; 15882 break; 15883 } elseif ($chardata[$j]['type'] != 'WS') { 15884 break; 15885 } 15886 ++$j; 15887 } 15888 } 15889 } 15890 15891 // Arabic Shaping 15892 // Cursively connected scripts, such as Arabic or Syriac, require the selection of positional character shapes that depend on adjacent characters. Shaping is logically applied after the Bidirectional Algorithm is used and is limited to characters within the same directional run. 15893 if ($arabic) { 15894 $endedletter = array(1569,1570,1571,1572,1573,1575,1577,1583,1584,1585,1586,1608,1688); 15895 $alfletter = array(1570,1571,1573,1575); 15896 $chardata2 = $chardata; 15897 $laaletter = false; 15898 $charAL = array(); 15899 $x = 0; 15900 for ($i=0; $i < $numchars; ++$i) { 15901 if (($this->unicode->uni_type[$chardata[$i]['char']] == 'AL') OR ($chardata[$i]['char'] == 32) OR ($chardata[$i]['char'] == 8204)) { 15902 $charAL[$x] = $chardata[$i]; 15903 $charAL[$x]['i'] = $i; 15904 $chardata[$i]['x'] = $x; 15905 ++$x; 15906 } 15907 } 15908 $numAL = $x; 15909 for ($i=0; $i < $numchars; ++$i) { 15910 $thischar = $chardata[$i]; 15911 if ($i > 0) { 15912 $prevchar = $chardata[($i-1)]; 15913 } else { 15914 $prevchar = false; 15915 } 15916 if (($i+1) < $numchars) { 15917 $nextchar = $chardata[($i+1)]; 15918 } else { 15919 $nextchar = false; 15920 } 15921 if ($this->unicode->uni_type[$thischar['char']] == 'AL') { 15922 $x = $thischar['x']; 15923 if ($x > 0) { 15924 $prevchar = $charAL[($x-1)]; 15925 } else { 15926 $prevchar = false; 15927 } 15928 if (($x+1) < $numAL) { 15929 $nextchar = $charAL[($x+1)]; 15930 } else { 15931 $nextchar = false; 15932 } 15933 // if laa letter 15934 if (($prevchar !== false) AND ($prevchar['char'] == 1604) AND (in_array($thischar['char'], $alfletter))) { 15935 $arabicarr = $this->unicode->uni_laa_array; 15936 $laaletter = true; 15937 if ($x > 1) { 15938 $prevchar = $charAL[($x-2)]; 15939 } else { 15940 $prevchar = false; 15941 } 15942 } else { 15943 $arabicarr = $this->unicode->uni_arabicsubst; 15944 $laaletter = false; 15945 } 15946 if (($prevchar !== false) AND ($nextchar !== false) AND 15947 (($this->unicode->uni_type[$prevchar['char']] == 'AL') OR ($this->unicode->uni_type[$prevchar['char']] == 'NSM')) AND 15948 (($this->unicode->uni_type[$nextchar['char']] == 'AL') OR ($this->unicode->uni_type[$nextchar['char']] == 'NSM')) AND 15949 ($prevchar['type'] == $thischar['type']) AND 15950 ($nextchar['type'] == $thischar['type']) AND 15951 ($nextchar['char'] != 1567)) { 15952 if (in_array($prevchar['char'], $endedletter)) { 15953 if (isset($arabicarr[$thischar['char']][2])) { 15954 // initial 15955 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2]; 15956 } 15957 } else { 15958 if (isset($arabicarr[$thischar['char']][3])) { 15959 // medial 15960 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][3]; 15961 } 15962 } 15963 } elseif (($nextchar !== false) AND 15964 (($this->unicode->uni_type[$nextchar['char']] == 'AL') OR ($this->unicode->uni_type[$nextchar['char']] == 'NSM')) AND 15965 ($nextchar['type'] == $thischar['type']) AND 15966 ($nextchar['char'] != 1567)) { 15967 if (isset($arabicarr[$chardata[$i]['char']][2])) { 15968 // initial 15969 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2]; 15970 } 15971 } elseif ((($prevchar !== false) AND 15972 (($this->unicode->uni_type[$prevchar['char']] == 'AL') OR ($this->unicode->uni_type[$prevchar['char']] == 'NSM')) AND 15973 ($prevchar['type'] == $thischar['type'])) OR 15974 (($nextchar !== false) AND ($nextchar['char'] == 1567))) { 15975 // final 15976 if (($i > 1) AND ($thischar['char'] == 1607) AND 15977 ($chardata[$i-1]['char'] == 1604) AND 15978 ($chardata[$i-2]['char'] == 1604)) { 15979 //Allah Word 15980 // mark characters to delete with false 15981 $chardata2[$i-2]['char'] = false; 15982 $chardata2[$i-1]['char'] = false; 15983 $chardata2[$i]['char'] = 65010; 15984 } else { 15985 if (($prevchar !== false) AND in_array($prevchar['char'], $endedletter)) { 15986 if (isset($arabicarr[$thischar['char']][0])) { 15987 // isolated 15988 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0]; 15989 } 15990 } else { 15991 if (isset($arabicarr[$thischar['char']][1])) { 15992 // final 15993 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][1]; 15994 } 15995 } 15996 } 15997 } elseif (isset($arabicarr[$thischar['char']][0])) { 15998 // isolated 15999 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0]; 16000 } 16001 // if laa letter 16002 if ($laaletter) { 16003 // mark characters to delete with false 16004 $chardata2[($charAL[($x-1)]['i'])]['char'] = false; 16005 } 16006 } // end if AL (Arabic Letter) 16007 } // end for each char 16008 /* 16009 * Combining characters that can occur with Arabic Shadda (0651 HEX, 1617 DEC) are replaced. 16010 * Putting the combining mark and shadda in the same glyph allows us to avoid the two marks overlapping each other in an illegible manner. 16011 */ 16012 for ($i = 0; $i < ($numchars-1); ++$i) { 16013 if (($chardata2[$i]['char'] == 1617) AND (isset($this->unicode->uni_diacritics[($chardata2[$i+1]['char'])]))) { 16014 // check if the subtitution font is defined on current font 16015 if (isset($this->CurrentFont['cw'][($this->unicode->uni_diacritics[($chardata2[$i+1]['char'])])])) { 16016 $chardata2[$i]['char'] = false; 16017 $chardata2[$i+1]['char'] = $this->unicode->uni_diacritics[($chardata2[$i+1]['char'])]; 16018 } 16019 } 16020 } 16021 // remove marked characters 16022 foreach ($chardata2 as $key => $value) { 16023 if ($value['char'] === false) { 16024 unset($chardata2[$key]); 16025 } 16026 } 16027 $chardata = array_values($chardata2); 16028 $numchars = count($chardata); 16029 unset($chardata2); 16030 unset($arabicarr); 16031 unset($laaletter); 16032 unset($charAL); 16033 } 16034 16035 // L2. From the highest level found in the text to the lowest odd level on each line, including intermediate levels not actually present in the text, reverse any contiguous sequence of characters that are at that level or higher. 16036 for ($j=$maxlevel; $j > 0; $j--) { 16037 $ordarray = Array(); 16038 $revarr = Array(); 16039 $onlevel = false; 16040 for ($i=0; $i < $numchars; ++$i) { 16041 if ($chardata[$i]['level'] >= $j) { 16042 $onlevel = true; 16043 if (isset($this->unicode->uni_mirror[$chardata[$i]['char']])) { 16044 // L4. A character is depicted by a mirrored glyph if and only if (a) the resolved directionality of that character is R, and (b) the Bidi_Mirrored property value of that character is true. 16045 $chardata[$i]['char'] = $this->unicode->uni_mirror[$chardata[$i]['char']]; 16046 } 16047 $revarr[] = $chardata[$i]; 16048 } else { 16049 if ($onlevel) { 16050 $revarr = array_reverse($revarr); 16051 $ordarray = array_merge($ordarray, $revarr); 16052 $revarr = Array(); 16053 $onlevel = false; 16054 } 16055 $ordarray[] = $chardata[$i]; 16056 } 16057 } 16058 if ($onlevel) { 16059 $revarr = array_reverse($revarr); 16060 $ordarray = array_merge($ordarray, $revarr); 16061 } 16062 $chardata = $ordarray; 16063 } 16064 16065 $ordarray = array(); 16066 for ($i=0; $i < $numchars; ++$i) { 16067 $ordarray[] = $chardata[$i]['char']; 16068 // store char values for subsetting 16069 $this->CurrentFont['subsetchars'][$chardata[$i]['char']] = true; 16070 } 16071 // update font subsetchars 16072 $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']); 16073 return $ordarray; 16074 } 16075 16076 // END OF BIDIRECTIONAL TEXT SECTION ------------------- 16077 16086 protected function encodeNameObject($name) { 16087 $escname = ''; 16088 $length = strlen($name); 16089 for ($i = 0; $i < $length; ++$i) { 16090 $chr = $name{$i}; 16091 if (preg_match('/[0-9a-zA-Z]/', $chr) == 1) { 16092 $escname .= $chr; 16093 } else { 16094 $escname .= sprintf('#%02X', ord($chr)); 16095 } 16096 } 16097 return $escname; 16098 } 16099 16111 public function setDestination($name, $y=-1, $page='') { 16112 // remove unsupported characters 16113 $name = $this->encodeNameObject($name); 16114 if ($this->empty_string($name)) { 16115 return false; 16116 } 16117 if ($y == -1) { 16118 $y = $this->GetY(); 16119 } 16120 if (empty($page)) { 16121 $page = $this->PageNo(); 16122 if (empty($page)) { 16123 return; 16124 } 16125 } 16126 $this->dests[$name] = array('y' => $y, 'p' => $page); 16127 return $name; 16128 } 16129 16137 public function getDestination() { 16138 return $this->dests; 16139 } 16140 16147 protected function _putdests() { 16148 if (empty($this->dests)) { 16149 return; 16150 } 16151 $this->n_dests = $this->_newobj(); 16152 $out = ' <<'; 16153 foreach($this->dests as $name => $o) { 16154 $out .= ' /'.$name.' '.sprintf('[%u 0 R /XYZ 0 %.2F null]', $this->page_obj_id[($o['p'])], ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k))); 16155 } 16156 $out .= ' >>'; 16157 $out .= "\n".'endobj'; 16158 $this->_out($out); 16159 } 16160 16171 public function setBookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0)) { 16172 $this->Bookmark($txt, $level, $y, $page, $style, $color); 16173 } 16174 16187 public function Bookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0)) { 16188 if ($level < 0) { 16189 $level = 0; 16190 } 16191 if (isset($this->outlines[0])) { 16192 $lastoutline = end($this->outlines); 16193 $maxlevel = $lastoutline['l'] + 1; 16194 } else { 16195 $maxlevel = 0; 16196 } 16197 if ($level > $maxlevel) { 16198 $level = $maxlevel; 16199 } 16200 if ($y == -1) { 16201 $y = $this->GetY(); 16202 } 16203 if (empty($page)) { 16204 $page = $this->PageNo(); 16205 if (empty($page)) { 16206 return; 16207 } 16208 } 16209 $this->outlines[] = array('t' => $txt, 'l' => $level, 'y' => $y, 'p' => $page, 's' => strtoupper($style), 'c' => $color); 16210 } 16211 16217 protected function sortBookmarks() { 16218 // get sorting columns 16219 $outline_p = array(); 16220 $outline_y = array(); 16221 foreach ($this->outlines as $key => $row) { 16222 $outline_p[$key] = $row['p']; 16223 $outline_k[$key] = $key; 16224 } 16225 // sort outlines by page and original position 16226 array_multisort($outline_p, SORT_NUMERIC, SORT_ASC, $outline_k, SORT_NUMERIC, SORT_ASC, $this->outlines); 16227 } 16228 16235 protected function _putbookmarks() { 16236 $nb = count($this->outlines); 16237 if ($nb == 0) { 16238 return; 16239 } 16240 // sort bookmarks 16241 $this->sortBookmarks(); 16242 $lru = array(); 16243 $level = 0; 16244 foreach ($this->outlines as $i => $o) { 16245 if ($o['l'] > 0) { 16246 $parent = $lru[($o['l'] - 1)]; 16247 //Set parent and last pointers 16248 $this->outlines[$i]['parent'] = $parent; 16249 $this->outlines[$parent]['last'] = $i; 16250 if ($o['l'] > $level) { 16251 //Level increasing: set first pointer 16252 $this->outlines[$parent]['first'] = $i; 16253 } 16254 } else { 16255 $this->outlines[$i]['parent'] = $nb; 16256 } 16257 if (($o['l'] <= $level) AND ($i > 0)) { 16258 //Set prev and next pointers 16259 $prev = $lru[$o['l']]; 16260 $this->outlines[$prev]['next'] = $i; 16261 $this->outlines[$i]['prev'] = $prev; 16262 } 16263 $lru[$o['l']] = $i; 16264 $level = $o['l']; 16265 } 16266 //Outline items 16267 $n = $this->n + 1; 16268 $nltags = '/<br[\s]?\/>|<\/(blockquote|dd|dl|div|dt|h1|h2|h3|h4|h5|h6|hr|li|ol|p|pre|ul|tcpdf|table|tr|td)>/si'; 16269 foreach ($this->outlines as $i => $o) { 16270 $oid = $this->_newobj(); 16271 // covert HTML title to string 16272 $title = preg_replace($nltags, "\n", $o['t']); 16273 $title = preg_replace("/[\r]+/si", '', $title); 16274 $title = preg_replace("/[\n]+/si", "\n", $title); 16275 $title = strip_tags($title); 16276 $title = $this->stringTrim($title); 16277 $out = '<</Title '.$this->_textstring($title, $oid); 16278 $out .= ' /Parent '.($n + $o['parent']).' 0 R'; 16279 if (isset($o['prev'])) { 16280 $out .= ' /Prev '.($n + $o['prev']).' 0 R'; 16281 } 16282 if (isset($o['next'])) { 16283 $out .= ' /Next '.($n + $o['next']).' 0 R'; 16284 } 16285 if (isset($o['first'])) { 16286 $out .= ' /First '.($n + $o['first']).' 0 R'; 16287 } 16288 if (isset($o['last'])) { 16289 $out .= ' /Last '.($n + $o['last']).' 0 R'; 16290 } 16291 if (isset($this->page_obj_id[($o['p'])])) { 16292 $out .= ' '.sprintf('/Dest [%u 0 R /XYZ 0 %.2F null]', $this->page_obj_id[($o['p'])], ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k))); 16293 } 16294 // set font style 16295 $style = 0; 16296 if (!empty($o['s'])) { 16297 // bold 16298 if (strpos($o['s'], 'B') !== false) { 16299 $style |= 2; 16300 } 16301 // oblique 16302 if (strpos($o['s'], 'I') !== false) { 16303 $style |= 1; 16304 } 16305 } 16306 $out .= sprintf(' /F %d', $style); 16307 // set bookmark color 16308 if (isset($o['c']) AND is_array($o['c']) AND (count($o['c']) == 3)) { 16309 $color = array_values($o['c']); 16310 $out .= sprintf(' /C [%.3F %.3F %.3F]', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255)); 16311 } else { 16312 // black 16313 $out .= ' /C [0.0 0.0 0.0]'; 16314 } 16315 $out .= ' /Count 0'; // normally closed item 16316 $out .= ' >>'; 16317 $out .= "\n".'endobj'; 16318 $this->_out($out); 16319 } 16320 //Outline root 16321 $this->OutlineRoot = $this->_newobj(); 16322 $this->_out('<< /Type /Outlines /First '.$n.' 0 R /Last '.($n + $lru[0]).' 0 R >>'."\n".'endobj'); 16323 } 16324 16325 // --- JAVASCRIPT ------------------------------------------------------ 16326 16334 public function IncludeJS($script) { 16335 $this->javascript .= $script; 16336 } 16337 16347 public function addJavascriptObject($script, $onload=false) { 16348 if ($this->pdfa_mode) { 16349 // javascript is not allowed in PDF/A mode 16350 return false; 16351 } 16352 ++$this->n; 16353 $this->js_objects[$this->n] = array('n' => $this->n, 'js' => $script, 'onload' => $onload); 16354 return $this->n; 16355 } 16356 16363 protected function _putjavascript() { 16364 if ($this->pdfa_mode OR (empty($this->javascript) AND empty($this->js_objects))) { 16365 return; 16366 } 16367 if (strpos($this->javascript, 'this.addField') > 0) { 16368 if (!$this->ur['enabled']) { 16369 //$this->setUserRights(); 16370 } 16371 // the following two lines are used to avoid form fields duplication after saving 16372 // The addField method only works when releasing user rights (UR3) 16373 $jsa = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%.2F,%.2F,%.2F,%.2F]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1); 16374 $jsb = "getField('tcpdfdocsaved').value='saved';"; 16375 $this->javascript = $jsa."\n".$this->javascript."\n".$jsb; 16376 } 16377 $this->n_js = $this->_newobj(); 16378 $out = ' << /Names ['; 16379 if (!empty($this->javascript)) { 16380 $out .= ' (EmbeddedJS) '.($this->n + 1).' 0 R'; 16381 } 16382 if (!empty($this->js_objects)) { 16383 foreach ($this->js_objects as $key => $val) { 16384 if ($val['onload']) { 16385 $out .= ' (JS'.$key.') '.$key.' 0 R'; 16386 } 16387 } 16388 } 16389 $out .= ' ] >>'; 16390 $out .= "\n".'endobj'; 16391 $this->_out($out); 16392 // default Javascript object 16393 if (!empty($this->javascript)) { 16394 $obj_id = $this->_newobj(); 16395 $out = '<< /S /JavaScript'; 16396 $out .= ' /JS '.$this->_textstring($this->javascript, $obj_id); 16397 $out .= ' >>'; 16398 $out .= "\n".'endobj'; 16399 $this->_out($out); 16400 } 16401 // additional Javascript objects 16402 if (!empty($this->js_objects)) { 16403 foreach ($this->js_objects as $key => $val) { 16404 $out = $this->_getobj($key)."\n".' << /S /JavaScript /JS '.$this->_textstring($val['js'], $key).' >>'."\n".'endobj'; 16405 $this->_out($out); 16406 } 16407 } 16408 } 16409 16417 protected function _JScolor($color) { 16418 static $aColors = array('transparent', 'black', 'white', 'red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'dkGray', 'gray', 'ltGray'); 16419 if (substr($color,0,1) == '#') { 16420 return sprintf("['RGB',%.3F,%.3F,%.3F]", hexdec(substr($color,1,2))/255, hexdec(substr($color,3,2))/255, hexdec(substr($color,5,2))/255); 16421 } 16422 if (!in_array($color,$aColors)) { 16423 $this->Error('Invalid color: '.$color); 16424 } 16425 return 'color.'.$color; 16426 } 16427 16441 protected function _addfield($type, $name, $x, $y, $w, $h, $prop) { 16442 if ($this->rtl) { 16443 $x = $x - $w; 16444 } 16445 // the followind avoid fields duplication after saving the document 16446 $this->javascript .= "if (getField('tcpdfdocsaved').value != 'saved') {"; 16447 $k = $this->k; 16448 $this->javascript .= sprintf("f".$name."=this.addField('%s','%s',%u,[%.2F,%.2F,%.2F,%.2F]);", $name, $type, $this->PageNo()-1, $x*$k, ($this->h-$y)*$k+1, ($x+$w)*$k, ($this->h-$y-$h)*$k+1)."\n"; 16449 $this->javascript .= 'f'.$name.'.textSize='.$this->FontSizePt.";\n"; 16450 while (list($key, $val) = each($prop)) { 16451 if (strcmp(substr($key, -5), 'Color') == 0) { 16452 $val = $this->_JScolor($val); 16453 } else { 16454 $val = "'".$val."'"; 16455 } 16456 $this->javascript .= 'f'.$name.'.'.$key.'='.$val.";\n"; 16457 } 16458 if ($this->rtl) { 16459 $this->x -= $w; 16460 } else { 16461 $this->x += $w; 16462 } 16463 $this->javascript .= '}'; 16464 } 16465 16466 // --- FORM FIELDS ----------------------------------------------------- 16467 16476 protected function getAnnotOptFromJSProp($prop) { 16477 if (isset($prop['aopt']) AND is_array($prop['aopt'])) { 16478 // the annotation options area lready defined 16479 return $prop['aopt']; 16480 } 16481 $opt = array(); // value to be returned 16482 // alignment: Controls how the text is laid out within the text field. 16483 if (isset($prop['alignment'])) { 16484 switch ($prop['alignment']) { 16485 case 'left': { 16486 $opt['q'] = 0; 16487 break; 16488 } 16489 case 'center': { 16490 $opt['q'] = 1; 16491 break; 16492 } 16493 case 'right': { 16494 $opt['q'] = 2; 16495 break; 16496 } 16497 default: { 16498 $opt['q'] = ($this->rtl)?2:0; 16499 break; 16500 } 16501 } 16502 } 16503 // lineWidth: Specifies the thickness of the border when stroking the perimeter of a field's rectangle. 16504 if (isset($prop['lineWidth'])) { 16505 $linewidth = intval($prop['lineWidth']); 16506 } else { 16507 $linewidth = 1; 16508 } 16509 // borderStyle: The border style for a field. 16510 if (isset($prop['borderStyle'])) { 16511 switch ($prop['borderStyle']) { 16512 case 'border.d': 16513 case 'dashed': { 16514 $opt['border'] = array(0, 0, $linewidth, array(3, 2)); 16515 $opt['bs'] = array('w'=>$linewidth, 's'=>'D', 'd'=>array(3, 2)); 16516 break; 16517 } 16518 case 'border.b': 16519 case 'beveled': { 16520 $opt['border'] = array(0, 0, $linewidth); 16521 $opt['bs'] = array('w'=>$linewidth, 's'=>'B'); 16522 break; 16523 } 16524 case 'border.i': 16525 case 'inset': { 16526 $opt['border'] = array(0, 0, $linewidth); 16527 $opt['bs'] = array('w'=>$linewidth, 's'=>'I'); 16528 break; 16529 } 16530 case 'border.u': 16531 case 'underline': { 16532 $opt['border'] = array(0, 0, $linewidth); 16533 $opt['bs'] = array('w'=>$linewidth, 's'=>'U'); 16534 break; 16535 } 16536 case 'border.s': 16537 case 'solid': { 16538 $opt['border'] = array(0, 0, $linewidth); 16539 $opt['bs'] = array('w'=>$linewidth, 's'=>'S'); 16540 break; 16541 } 16542 default: { 16543 break; 16544 } 16545 } 16546 } 16547 if (isset($prop['border']) AND is_array($prop['border'])) { 16548 $opt['border'] = $prop['border']; 16549 } 16550 if (!isset($opt['mk'])) { 16551 $opt['mk'] = array(); 16552 } 16553 if (!isset($opt['mk']['if'])) { 16554 $opt['mk']['if'] = array(); 16555 } 16556 $opt['mk']['if']['a'] = array(0.5, 0.5); 16557 // buttonAlignX: Controls how space is distributed from the left of the button face with respect to the icon. 16558 if (isset($prop['buttonAlignX'])) { 16559 $opt['mk']['if']['a'][0] = $prop['buttonAlignX']; 16560 } 16561 // buttonAlignY: Controls how unused space is distributed from the bottom of the button face with respect to the icon. 16562 if (isset($prop['buttonAlignY'])) { 16563 $opt['mk']['if']['a'][1] = $prop['buttonAlignY']; 16564 } 16565 // buttonFitBounds: If true, the extent to which the icon may be scaled is set to the bounds of the button field. 16566 if (isset($prop['buttonFitBounds']) AND ($prop['buttonFitBounds'] == 'true')) { 16567 $opt['mk']['if']['fb'] = true; 16568 } 16569 // buttonScaleHow: Controls how the icon is scaled (if necessary) to fit inside the button face. 16570 if (isset($prop['buttonScaleHow'])) { 16571 switch ($prop['buttonScaleHow']) { 16572 case 'scaleHow.proportional': { 16573 $opt['mk']['if']['s'] = 'P'; 16574 break; 16575 } 16576 case 'scaleHow.anamorphic': { 16577 $opt['mk']['if']['s'] = 'A'; 16578 break; 16579 } 16580 } 16581 } 16582 // buttonScaleWhen: Controls when an icon is scaled to fit inside the button face. 16583 if (isset($prop['buttonScaleWhen'])) { 16584 switch ($prop['buttonScaleWhen']) { 16585 case 'scaleWhen.always': { 16586 $opt['mk']['if']['sw'] = 'A'; 16587 break; 16588 } 16589 case 'scaleWhen.never': { 16590 $opt['mk']['if']['sw'] = 'N'; 16591 break; 16592 } 16593 case 'scaleWhen.tooBig': { 16594 $opt['mk']['if']['sw'] = 'B'; 16595 break; 16596 } 16597 case 'scaleWhen.tooSmall': { 16598 $opt['mk']['if']['sw'] = 'S'; 16599 break; 16600 } 16601 } 16602 } 16603 // buttonPosition: Controls how the text and the icon of the button are positioned with respect to each other within the button face. 16604 if (isset($prop['buttonPosition'])) { 16605 switch ($prop['buttonPosition']) { 16606 case 0: 16607 case 'position.textOnly': { 16608 $opt['mk']['tp'] = 0; 16609 break; 16610 } 16611 case 1: 16612 case 'position.iconOnly': { 16613 $opt['mk']['tp'] = 1; 16614 break; 16615 } 16616 case 2: 16617 case 'position.iconTextV': { 16618 $opt['mk']['tp'] = 2; 16619 break; 16620 } 16621 case 3: 16622 case 'position.textIconV': { 16623 $opt['mk']['tp'] = 3; 16624 break; 16625 } 16626 case 4: 16627 case 'position.iconTextH': { 16628 $opt['mk']['tp'] = 4; 16629 break; 16630 } 16631 case 5: 16632 case 'position.textIconH': { 16633 $opt['mk']['tp'] = 5; 16634 break; 16635 } 16636 case 6: 16637 case 'position.overlay': { 16638 $opt['mk']['tp'] = 6; 16639 break; 16640 } 16641 } 16642 } 16643 // fillColor: Specifies the background color for a field. 16644 if (isset($prop['fillColor'])) { 16645 if (is_array($prop['fillColor'])) { 16646 $opt['mk']['bg'] = $prop['fillColor']; 16647 } else { 16648 $opt['mk']['bg'] = $this->convertHTMLColorToDec($prop['fillColor']); 16649 } 16650 } 16651 // strokeColor: Specifies the stroke color for a field that is used to stroke the rectangle of the field with a line as large as the line width. 16652 if (isset($prop['strokeColor'])) { 16653 if (is_array($prop['strokeColor'])) { 16654 $opt['mk']['bc'] = $prop['strokeColor']; 16655 } else { 16656 $opt['mk']['bc'] = $this->convertHTMLColorToDec($prop['strokeColor']); 16657 } 16658 } 16659 // rotation: The rotation of a widget in counterclockwise increments. 16660 if (isset($prop['rotation'])) { 16661 $opt['mk']['r'] = $prop['rotation']; 16662 } 16663 // charLimit: Limits the number of characters that a user can type into a text field. 16664 if (isset($prop['charLimit'])) { 16665 $opt['maxlen'] = intval($prop['charLimit']); 16666 } 16667 if (!isset($ff)) { 16668 $ff = 0; // default value 16669 } 16670 // readonly: The read-only characteristic of a field. If a field is read-only, the user can see the field but cannot change it. 16671 if (isset($prop['readonly']) AND ($prop['readonly'] == 'true')) { 16672 $ff += 1 << 0; 16673 } 16674 // required: Specifies whether a field requires a value. 16675 if (isset($prop['required']) AND ($prop['required'] == 'true')) { 16676 $ff += 1 << 1; 16677 } 16678 // multiline: Controls how text is wrapped within the field. 16679 if (isset($prop['multiline']) AND ($prop['multiline'] == 'true')) { 16680 $ff += 1 << 12; 16681 } 16682 // password: Specifies whether the field should display asterisks when data is entered in the field. 16683 if (isset($prop['password']) AND ($prop['password'] == 'true')) { 16684 $ff += 1 << 13; 16685 } 16686 // NoToggleToOff: If set, exactly one radio button shall be selected at all times; selecting the currently selected button has no effect. 16687 if (isset($prop['NoToggleToOff']) AND ($prop['NoToggleToOff'] == 'true')) { 16688 $ff += 1 << 14; 16689 } 16690 // Radio: If set, the field is a set of radio buttons. 16691 if (isset($prop['Radio']) AND ($prop['Radio'] == 'true')) { 16692 $ff += 1 << 15; 16693 } 16694 // Pushbutton: If set, the field is a pushbutton that does not retain a permanent value. 16695 if (isset($prop['Pushbutton']) AND ($prop['Pushbutton'] == 'true')) { 16696 $ff += 1 << 16; 16697 } 16698 // Combo: If set, the field is a combo box; if clear, the field is a list box. 16699 if (isset($prop['Combo']) AND ($prop['Combo'] == 'true')) { 16700 $ff += 1 << 17; 16701 } 16702 // editable: Controls whether a combo box is editable. 16703 if (isset($prop['editable']) AND ($prop['editable'] == 'true')) { 16704 $ff += 1 << 18; 16705 } 16706 // Sort: If set, the field's option items shall be sorted alphabetically. 16707 if (isset($prop['Sort']) AND ($prop['Sort'] == 'true')) { 16708 $ff += 1 << 19; 16709 } 16710 // fileSelect: If true, sets the file-select flag in the Options tab of the text field (Field is Used for File Selection). 16711 if (isset($prop['fileSelect']) AND ($prop['fileSelect'] == 'true')) { 16712 $ff += 1 << 20; 16713 } 16714 // multipleSelection: If true, indicates that a list box allows a multiple selection of items. 16715 if (isset($prop['multipleSelection']) AND ($prop['multipleSelection'] == 'true')) { 16716 $ff += 1 << 21; 16717 } 16718 // doNotSpellCheck: If true, spell checking is not performed on this editable text field. 16719 if (isset($prop['doNotSpellCheck']) AND ($prop['doNotSpellCheck'] == 'true')) { 16720 $ff += 1 << 22; 16721 } 16722 // doNotScroll: If true, the text field does not scroll and the user, therefore, is limited by the rectangular region designed for the field. 16723 if (isset($prop['doNotScroll']) AND ($prop['doNotScroll'] == 'true')) { 16724 $ff += 1 << 23; 16725 } 16726 // comb: If set to true, the field background is drawn as series of boxes (one for each character in the value of the field) and each character of the content is drawn within those boxes. The number of boxes drawn is determined from the charLimit property. It applies only to text fields. The setter will also raise if any of the following field properties are also set multiline, password, and fileSelect. A side-effect of setting this property is that the doNotScroll property is also set. 16727 if (isset($prop['comb']) AND ($prop['comb'] == 'true')) { 16728 $ff += 1 << 24; 16729 } 16730 // radiosInUnison: If false, even if a group of radio buttons have the same name and export value, they behave in a mutually exclusive fashion, like HTML radio buttons. 16731 if (isset($prop['radiosInUnison']) AND ($prop['radiosInUnison'] == 'true')) { 16732 $ff += 1 << 25; 16733 } 16734 // richText: If true, the field allows rich text formatting. 16735 if (isset($prop['richText']) AND ($prop['richText'] == 'true')) { 16736 $ff += 1 << 25; 16737 } 16738 // commitOnSelChange: Controls whether a field value is committed after a selection change. 16739 if (isset($prop['commitOnSelChange']) AND ($prop['commitOnSelChange'] == 'true')) { 16740 $ff += 1 << 26; 16741 } 16742 $opt['ff'] = $ff; 16743 // defaultValue: The default value of a field - that is, the value that the field is set to when the form is reset. 16744 if (isset($prop['defaultValue'])) { 16745 $opt['dv'] = $prop['defaultValue']; 16746 } 16747 $f = 4; // default value for annotation flags 16748 // readonly: The read-only characteristic of a field. If a field is read-only, the user can see the field but cannot change it. 16749 if (isset($prop['readonly']) AND ($prop['readonly'] == 'true')) { 16750 $f += 1 << 6; 16751 } 16752 // display: Controls whether the field is hidden or visible on screen and in print. 16753 if (isset($prop['display'])) { 16754 if ($prop['display'] == 'display.visible') { 16755 // 16756 } elseif ($prop['display'] == 'display.hidden') { 16757 $f += 1 << 1; 16758 } elseif ($prop['display'] == 'display.noPrint') { 16759 $f -= 1 << 2; 16760 } elseif ($prop['display'] == 'display.noView') { 16761 $f += 1 << 5; 16762 } 16763 } 16764 $opt['f'] = $f; 16765 // currentValueIndices: Reads and writes single or multiple values of a list box or combo box. 16766 if (isset($prop['currentValueIndices']) AND is_array($prop['currentValueIndices'])) { 16767 $opt['i'] = $prop['currentValueIndices']; 16768 } 16769 // value: The value of the field data that the user has entered. 16770 if (isset($prop['value'])) { 16771 if (is_array($prop['value'])) { 16772 $opt['opt'] = array(); 16773 foreach ($prop['value'] AS $key => $optval) { 16774 // exportValues: An array of strings representing the export values for the field. 16775 if (isset($prop['exportValues'][$key])) { 16776 $opt['opt'][$key] = array($prop['exportValues'][$key], $prop['value'][$key]); 16777 } else { 16778 $opt['opt'][$key] = $prop['value'][$key]; 16779 } 16780 } 16781 } else { 16782 $opt['v'] = $prop['value']; 16783 } 16784 } 16785 // richValue: This property specifies the text contents and formatting of a rich text field. 16786 if (isset($prop['richValue'])) { 16787 $opt['rv'] = $prop['richValue']; 16788 } 16789 // submitName: If nonempty, used during form submission instead of name. Only applicable if submitting in HTML format (that is, URL-encoded). 16790 if (isset($prop['submitName'])) { 16791 $opt['tm'] = $prop['submitName']; 16792 } 16793 // name: Fully qualified field name. 16794 if (isset($prop['name'])) { 16795 $opt['t'] = $prop['name']; 16796 } 16797 // userName: The user name (short description string) of the field. 16798 if (isset($prop['userName'])) { 16799 $opt['tu'] = $prop['userName']; 16800 } 16801 // highlight: Defines how a button reacts when a user clicks it. 16802 if (isset($prop['highlight'])) { 16803 switch ($prop['highlight']) { 16804 case 'none': 16805 case 'highlight.n': { 16806 $opt['h'] = 'N'; 16807 break; 16808 } 16809 case 'invert': 16810 case 'highlight.i': { 16811 $opt['h'] = 'i'; 16812 break; 16813 } 16814 case 'push': 16815 case 'highlight.p': { 16816 $opt['h'] = 'P'; 16817 break; 16818 } 16819 case 'outline': 16820 case 'highlight.o': { 16821 $opt['h'] = 'O'; 16822 break; 16823 } 16824 } 16825 } 16826 // Unsupported options: 16827 // - calcOrderIndex: Changes the calculation order of fields in the document. 16828 // - delay: Delays the redrawing of a field's appearance. 16829 // - defaultStyle: This property defines the default style attributes for the form field. 16830 // - style: Allows the user to set the glyph style of a check box or radio button. 16831 // - textColor, textFont, textSize 16832 return $opt; 16833 } 16834 16842 public function setFormDefaultProp($prop=array()) { 16843 $this->default_form_prop = $prop; 16844 } 16845 16853 public function getFormDefaultProp() { 16854 return $this->default_form_prop; 16855 } 16856 16871 public function TextField($name, $w, $h, $prop=array(), $opt=array(), $x='', $y='', $js=false) { 16872 if ($x === '') { 16873 $x = $this->x; 16874 } 16875 if ($y === '') { 16876 $y = $this->y; 16877 } 16878 // check page for no-write regions and adapt page margins if necessary 16879 list($x, $y) = $this->checkPageRegions($h, $x, $y); 16880 if ($js) { 16881 $this->_addfield('text', $name, $x, $y, $w, $h, $prop); 16882 return; 16883 } 16884 // get default style 16885 $prop = array_merge($this->getFormDefaultProp(), $prop); 16886 // get annotation data 16887 $popt = $this->getAnnotOptFromJSProp($prop); 16888 // set default appearance stream 16889 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i']; 16890 $fontstyle = sprintf('/F%d %.2F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor); 16891 $popt['da'] = $fontstyle; 16892 // build appearance stream 16893 $popt['ap'] = array(); 16894 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' '; 16895 $text = ''; 16896 if (isset($prop['value']) AND !empty($prop['value'])) { 16897 $text = $prop['value']; 16898 } elseif (isset($opt['v']) AND !empty($opt['v'])) { 16899 $text = $opt['v']; 16900 } 16901 $tmpid = $this->startTemplate($w, $h, false); 16902 $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false); 16903 $this->endTemplate(); 16904 --$this->n; 16905 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata']; 16906 unset($this->xobjects[$tmpid]); 16907 $popt['ap']['n'] .= 'Q EMC'; 16908 // merge options 16909 $opt = array_merge($popt, $opt); 16910 // remove some conflicting options 16911 unset($opt['bs']); 16912 // set remaining annotation data 16913 $opt['Subtype'] = 'Widget'; 16914 $opt['ft'] = 'Tx'; 16915 $opt['t'] = $name; 16916 // Additional annotation's parameters (check _putannotsobj() method): 16917 //$opt['f'] 16918 //$opt['as'] 16919 //$opt['bs'] 16920 //$opt['be'] 16921 //$opt['c'] 16922 //$opt['border'] 16923 //$opt['h'] 16924 //$opt['mk']; 16925 //$opt['mk']['r'] 16926 //$opt['mk']['bc']; 16927 //$opt['mk']['bg']; 16928 unset($opt['mk']['ca']); 16929 unset($opt['mk']['rc']); 16930 unset($opt['mk']['ac']); 16931 unset($opt['mk']['i']); 16932 unset($opt['mk']['ri']); 16933 unset($opt['mk']['ix']); 16934 unset($opt['mk']['if']); 16935 //$opt['mk']['if']['sw']; 16936 //$opt['mk']['if']['s']; 16937 //$opt['mk']['if']['a']; 16938 //$opt['mk']['if']['fb']; 16939 unset($opt['mk']['tp']); 16940 //$opt['tu'] 16941 //$opt['tm'] 16942 //$opt['ff'] 16943 //$opt['v'] 16944 //$opt['dv'] 16945 //$opt['a'] 16946 //$opt['aa'] 16947 //$opt['q'] 16948 $this->Annotation($x, $y, $w, $h, $name, $opt, 0); 16949 if ($this->rtl) { 16950 $this->x -= $w; 16951 } else { 16952 $this->x += $w; 16953 } 16954 } 16955 16971 public function RadioButton($name, $w, $prop=array(), $opt=array(), $onvalue='On', $checked=false, $x='', $y='', $js=false) { 16972 if ($x === '') { 16973 $x = $this->x; 16974 } 16975 if ($y === '') { 16976 $y = $this->y; 16977 } 16978 // check page for no-write regions and adapt page margins if necessary 16979 list($x, $y) = $this->checkPageRegions($w, $x, $y); 16980 if ($js) { 16981 $this->_addfield('radiobutton', $name, $x, $y, $w, $w, $prop); 16982 return; 16983 } 16984 if ($this->empty_string($onvalue)) { 16985 $onvalue = 'On'; 16986 } 16987 if ($checked) { 16988 $defval = $onvalue; 16989 } else { 16990 $defval = 'Off'; 16991 } 16992 // set font 16993 $font = 'zapfdingbats'; 16994 $this->AddFont($font); 16995 $tmpfont = $this->getFontBuffer($font); 16996 // set data for parent group 16997 if (!isset($this->radiobutton_groups[$this->page])) { 16998 $this->radiobutton_groups[$this->page] = array(); 16999 } 17000 if (!isset($this->radiobutton_groups[$this->page][$name])) { 17001 $this->radiobutton_groups[$this->page][$name] = array(); 17002 ++$this->n; 17003 $this->radiobutton_groups[$this->page][$name]['n'] = $this->n; 17004 $this->radio_groups[] = $this->n; 17005 } 17006 $kid = ($this->n + 1); 17007 // save object ID to be added on Kids entry on parent object 17008 $this->radiobutton_groups[$this->page][$name][] = array('kid' => $kid, 'def' => $defval); 17009 // get default style 17010 $prop = array_merge($this->getFormDefaultProp(), $prop); 17011 $prop['NoToggleToOff'] = 'true'; 17012 $prop['Radio'] = 'true'; 17013 $prop['borderStyle'] = 'inset'; 17014 // get annotation data 17015 $popt = $this->getAnnotOptFromJSProp($prop); 17016 // set additional default options 17017 $this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i']; 17018 $fontstyle = sprintf('/F%d %.2F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor); 17019 $popt['da'] = $fontstyle; 17020 // build appearance stream 17021 $popt['ap'] = array(); 17022 $popt['ap']['n'] = array(); 17023 $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k); 17024 $popt['ap']['n'][$onvalue] = sprintf('q %s BT /F%d %.2F Tf %.2F %.2F Td ('.chr(108).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, 0, $fy); 17025 $popt['ap']['n']['Off'] = sprintf('q %s BT /F%d %.2F Tf %.2F %.2F Td ('.chr(109).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, 0, $fy); 17026 if (!isset($popt['mk'])) { 17027 $popt['mk'] = array(); 17028 } 17029 $popt['mk']['ca'] = '(l)'; 17030 // merge options 17031 $opt = array_merge($popt, $opt); 17032 // set remaining annotation data 17033 $opt['Subtype'] = 'Widget'; 17034 $opt['ft'] = 'Btn'; 17035 if ($checked) { 17036 $opt['v'] = array('/'.$onvalue); 17037 $opt['as'] = $onvalue; 17038 } else { 17039 $opt['as'] = 'Off'; 17040 } 17041 // store readonly flag 17042 if (!isset($this->radiobutton_groups[$this->page][$name]['#readonly#'])) { 17043 $this->radiobutton_groups[$this->page][$name]['#readonly#'] = false; 17044 } 17045 $this->radiobutton_groups[$this->page][$name]['#readonly#'] |= ($opt['f'] & 64); 17046 $this->Annotation($x, $y, $w, $w, $name, $opt, 0); 17047 if ($this->rtl) { 17048 $this->x -= $w; 17049 } else { 17050 $this->x += $w; 17051 } 17052 } 17053 17069 public function ListBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) { 17070 if ($x === '') { 17071 $x = $this->x; 17072 } 17073 if ($y === '') { 17074 $y = $this->y; 17075 } 17076 // check page for no-write regions and adapt page margins if necessary 17077 list($x, $y) = $this->checkPageRegions($h, $x, $y); 17078 if ($js) { 17079 $this->_addfield('listbox', $name, $x, $y, $w, $h, $prop); 17080 $s = ''; 17081 foreach ($values as $value) { 17082 $s .= '\''.addslashes($value).'\','; 17083 } 17084 $this->javascript .= 'f'.$name.'.setItems(['.substr($s, 0, -1)."]);\n"; 17085 return; 17086 } 17087 // get default style 17088 $prop = array_merge($this->getFormDefaultProp(), $prop); 17089 // get annotation data 17090 $popt = $this->getAnnotOptFromJSProp($prop); 17091 // set additional default values 17092 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i']; 17093 $fontstyle = sprintf('/F%d %.2F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor); 17094 $popt['da'] = $fontstyle; 17095 // build appearance stream 17096 $popt['ap'] = array(); 17097 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' '; 17098 $text = ''; 17099 foreach($values as $item) { 17100 $text .= $item."\n"; 17101 } 17102 $tmpid = $this->startTemplate($w, $h, false); 17103 $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false); 17104 $this->endTemplate(); 17105 --$this->n; 17106 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata']; 17107 unset($this->xobjects[$tmpid]); 17108 $popt['ap']['n'] .= 'Q EMC'; 17109 // merge options 17110 $opt = array_merge($popt, $opt); 17111 // set remaining annotation data 17112 $opt['Subtype'] = 'Widget'; 17113 $opt['ft'] = 'Ch'; 17114 $opt['t'] = $name; 17115 $opt['opt'] = $values; 17116 unset($opt['mk']['ca']); 17117 unset($opt['mk']['rc']); 17118 unset($opt['mk']['ac']); 17119 unset($opt['mk']['i']); 17120 unset($opt['mk']['ri']); 17121 unset($opt['mk']['ix']); 17122 unset($opt['mk']['if']); 17123 unset($opt['mk']['tp']); 17124 $this->Annotation($x, $y, $w, $h, $name, $opt, 0); 17125 if ($this->rtl) { 17126 $this->x -= $w; 17127 } else { 17128 $this->x += $w; 17129 } 17130 } 17131 17147 public function ComboBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) { 17148 if ($x === '') { 17149 $x = $this->x; 17150 } 17151 if ($y === '') { 17152 $y = $this->y; 17153 } 17154 // check page for no-write regions and adapt page margins if necessary 17155 list($x, $y) = $this->checkPageRegions($h, $x, $y); 17156 if ($js) { 17157 $this->_addfield('combobox', $name, $x, $y, $w, $h, $prop); 17158 $s = ''; 17159 foreach ($values as $value) { 17160 $s .= "'".addslashes($value)."',"; 17161 } 17162 $this->javascript .= 'f'.$name.'.setItems(['.substr($s, 0, -1)."]);\n"; 17163 return; 17164 } 17165 // get default style 17166 $prop = array_merge($this->getFormDefaultProp(), $prop); 17167 $prop['Combo'] = true; 17168 // get annotation data 17169 $popt = $this->getAnnotOptFromJSProp($prop); 17170 // set additional default options 17171 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i']; 17172 $fontstyle = sprintf('/F%d %.2F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor); 17173 $popt['da'] = $fontstyle; 17174 // build appearance stream 17175 $popt['ap'] = array(); 17176 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' '; 17177 $text = ''; 17178 foreach($values as $item) { 17179 $text .= $item[1]."\n"; 17180 } 17181 $tmpid = $this->startTemplate($w, $h, false); 17182 $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false); 17183 $this->endTemplate(); 17184 --$this->n; 17185 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata']; 17186 unset($this->xobjects[$tmpid]); 17187 $popt['ap']['n'] .= 'Q EMC'; 17188 // merge options 17189 $opt = array_merge($popt, $opt); 17190 // set remaining annotation data 17191 $opt['Subtype'] = 'Widget'; 17192 $opt['ft'] = 'Ch'; 17193 $opt['t'] = $name; 17194 $opt['opt'] = $values; 17195 unset($opt['mk']['ca']); 17196 unset($opt['mk']['rc']); 17197 unset($opt['mk']['ac']); 17198 unset($opt['mk']['i']); 17199 unset($opt['mk']['ri']); 17200 unset($opt['mk']['ix']); 17201 unset($opt['mk']['if']); 17202 unset($opt['mk']['tp']); 17203 $this->Annotation($x, $y, $w, $h, $name, $opt, 0); 17204 if ($this->rtl) { 17205 $this->x -= $w; 17206 } else { 17207 $this->x += $w; 17208 } 17209 } 17210 17226 public function CheckBox($name, $w, $checked=false, $prop=array(), $opt=array(), $onvalue='Yes', $x='', $y='', $js=false) { 17227 if ($x === '') { 17228 $x = $this->x; 17229 } 17230 if ($y === '') { 17231 $y = $this->y; 17232 } 17233 // check page for no-write regions and adapt page margins if necessary 17234 list($x, $y) = $this->checkPageRegions($w, $x, $y); 17235 if ($js) { 17236 $this->_addfield('checkbox', $name, $x, $y, $w, $w, $prop); 17237 return; 17238 } 17239 if (!isset($prop['value'])) { 17240 $prop['value'] = array('Yes'); 17241 } 17242 // get default style 17243 $prop = array_merge($this->getFormDefaultProp(), $prop); 17244 $prop['borderStyle'] = 'inset'; 17245 // get annotation data 17246 $popt = $this->getAnnotOptFromJSProp($prop); 17247 // set additional default options 17248 $font = 'zapfdingbats'; 17249 $this->AddFont($font); 17250 $tmpfont = $this->getFontBuffer($font); 17251 $this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i']; 17252 $fontstyle = sprintf('/F%d %.2F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor); 17253 $popt['da'] = $fontstyle; 17254 // build appearance stream 17255 $popt['ap'] = array(); 17256 $popt['ap']['n'] = array(); 17257 $fy = ((($tmpfont['desc']['Ascent'] + $tmpfont['desc']['Descent']) * $this->FontSizePt) / (1000 * $this->k)); 17258 $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k); 17259 $popt['ap']['n']['Yes'] = sprintf('q %s BT /F%d %.2F Tf %.2F %.2F Td ('.chr(110).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, 0, $fy); 17260 $popt['ap']['n']['Off'] = sprintf('q %s BT /F%d %.2F Tf %.2F %.2F Td ('.chr(111).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, 0, $fy); 17261 // merge options 17262 $opt = array_merge($popt, $opt); 17263 // set remaining annotation data 17264 $opt['Subtype'] = 'Widget'; 17265 $opt['ft'] = 'Btn'; 17266 $opt['t'] = $name; 17267 $opt['opt'] = array($onvalue); 17268 if ($checked) { 17269 $opt['v'] = array('/Yes'); 17270 $opt['as'] = 'Yes'; 17271 } else { 17272 $opt['v'] = array('/Off'); 17273 $opt['as'] = 'Off'; 17274 } 17275 $this->Annotation($x, $y, $w, $w, $name, $opt, 0); 17276 if ($this->rtl) { 17277 $this->x -= $w; 17278 } else { 17279 $this->x += $w; 17280 } 17281 } 17282 17299 public function Button($name, $w, $h, $caption, $action, $prop=array(), $opt=array(), $x='', $y='', $js=false) { 17300 if ($x === '') { 17301 $x = $this->x; 17302 } 17303 if ($y === '') { 17304 $y = $this->y; 17305 } 17306 // check page for no-write regions and adapt page margins if necessary 17307 list($x, $y) = $this->checkPageRegions($h, $x, $y); 17308 if ($js) { 17309 $this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop); 17310 $this->javascript .= 'f'.$name.".buttonSetCaption('".addslashes($caption)."');\n"; 17311 $this->javascript .= 'f'.$name.".setAction('MouseUp','".addslashes($action)."');\n"; 17312 $this->javascript .= 'f'.$name.".highlight='push';\n"; 17313 $this->javascript .= 'f'.$name.".print=false;\n"; 17314 return; 17315 } 17316 // get default style 17317 $prop = array_merge($this->getFormDefaultProp(), $prop); 17318 $prop['Pushbutton'] = 'true'; 17319 $prop['highlight'] = 'push'; 17320 $prop['display'] = 'display.noPrint'; 17321 // get annotation data 17322 $popt = $this->getAnnotOptFromJSProp($prop); 17323 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i']; 17324 $fontstyle = sprintf('/F%d %.2F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor); 17325 $popt['da'] = $fontstyle; 17326 // build appearance stream 17327 $popt['ap'] = array(); 17328 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' '; 17329 $tmpid = $this->startTemplate($w, $h, false); 17330 $bw = (2 / $this->k); // border width 17331 $border = array( 17332 'L' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)), 17333 'R' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)), 17334 'T' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)), 17335 'B' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51))); 17336 $this->SetFillColor(204); 17337 $this->Cell($w, $h, $caption, $border, 0, 'C', true, '', 1, false, 'T', 'M'); 17338 $this->endTemplate(); 17339 --$this->n; 17340 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata']; 17341 unset($this->xobjects[$tmpid]); 17342 $popt['ap']['n'] .= 'Q EMC'; 17343 // set additional default options 17344 if (!isset($popt['mk'])) { 17345 $popt['mk'] = array(); 17346 } 17347 $ann_obj_id = ($this->n + 1); 17348 if (!empty($action) AND !is_array($action)) { 17349 $ann_obj_id = ($this->n + 2); 17350 } 17351 $popt['mk']['ca'] = $this->_textstring($caption, $ann_obj_id); 17352 $popt['mk']['rc'] = $this->_textstring($caption, $ann_obj_id); 17353 $popt['mk']['ac'] = $this->_textstring($caption, $ann_obj_id); 17354 // merge options 17355 $opt = array_merge($popt, $opt); 17356 // set remaining annotation data 17357 $opt['Subtype'] = 'Widget'; 17358 $opt['ft'] = 'Btn'; 17359 $opt['t'] = $caption; 17360 $opt['v'] = $name; 17361 if (!empty($action)) { 17362 if (is_array($action)) { 17363 // form action options as on section 12.7.5 of PDF32000_2008. 17364 $opt['aa'] = '/D <<'; 17365 $bmode = array('SubmitForm', 'ResetForm', 'ImportData'); 17366 foreach ($action AS $key => $val) { 17367 if (($key == 'S') AND in_array($val, $bmode)) { 17368 $opt['aa'] .= ' /S /'.$val; 17369 } elseif (($key == 'F') AND (!empty($val))) { 17370 $opt['aa'] .= ' /F '.$this->_datastring($val, $ann_obj_id); 17371 } elseif (($key == 'Fields') AND is_array($val) AND !empty($val)) { 17372 $opt['aa'] .= ' /Fields ['; 17373 foreach ($val AS $field) { 17374 $opt['aa'] .= ' '.$this->_textstring($field, $ann_obj_id); 17375 } 17376 $opt['aa'] .= ']'; 17377 } elseif (($key == 'Flags')) { 17378 $ff = 0; 17379 if (is_array($val)) { 17380 foreach ($val AS $flag) { 17381 switch ($flag) { 17382 case 'Include/Exclude': { 17383 $ff += 1 << 0; 17384 break; 17385 } 17386 case 'IncludeNoValueFields': { 17387 $ff += 1 << 1; 17388 break; 17389 } 17390 case 'ExportFormat': { 17391 $ff += 1 << 2; 17392 break; 17393 } 17394 case 'GetMethod': { 17395 $ff += 1 << 3; 17396 break; 17397 } 17398 case 'SubmitCoordinates': { 17399 $ff += 1 << 4; 17400 break; 17401 } 17402 case 'XFDF': { 17403 $ff += 1 << 5; 17404 break; 17405 } 17406 case 'IncludeAppendSaves': { 17407 $ff += 1 << 6; 17408 break; 17409 } 17410 case 'IncludeAnnotations': { 17411 $ff += 1 << 7; 17412 break; 17413 } 17414 case 'SubmitPDF': { 17415 $ff += 1 << 8; 17416 break; 17417 } 17418 case 'CanonicalFormat': { 17419 $ff += 1 << 9; 17420 break; 17421 } 17422 case 'ExclNonUserAnnots': { 17423 $ff += 1 << 10; 17424 break; 17425 } 17426 case 'ExclFKey': { 17427 $ff += 1 << 11; 17428 break; 17429 } 17430 case 'EmbedForm': { 17431 $ff += 1 << 13; 17432 break; 17433 } 17434 } 17435 } 17436 } else { 17437 $ff = intval($val); 17438 } 17439 $opt['aa'] .= ' /Flags '.$ff; 17440 } 17441 } 17442 $opt['aa'] .= ' >>'; 17443 } else { 17444 // Javascript action or raw action command 17445 $js_obj_id = $this->addJavascriptObject($action); 17446 $opt['aa'] = '/D '.$js_obj_id.' 0 R'; 17447 } 17448 } 17449 $this->Annotation($x, $y, $w, $h, $name, $opt, 0); 17450 if ($this->rtl) { 17451 $this->x -= $w; 17452 } else { 17453 $this->x += $w; 17454 } 17455 } 17456 17457 // --- END FORMS FIELDS ------------------------------------------------ 17458 17466 protected function _putsignature() { 17467 if ((!$this->sign) OR (!isset($this->signature_data['cert_type']))) { 17468 return; 17469 } 17470 $sigobjid = ($this->sig_obj_id + 1); 17471 $out = $this->_getobj($sigobjid)."\n"; 17472 $out .= '<< /Type /Sig'; 17473 $out .= ' /Filter /Adobe.PPKLite'; 17474 $out .= ' /SubFilter /adbe.pkcs7.detached'; 17475 $out .= ' '.$this->byterange_string; 17476 $out .= ' /Contents<'.str_repeat('0', $this->signature_max_length).'>'; 17477 $out .= ' /Reference ['; // array of signature reference dictionaries 17478 $out .= ' << /Type /SigRef'; 17479 if ($this->signature_data['cert_type'] > 0) { 17480 $out .= ' /TransformMethod /DocMDP'; 17481 $out .= ' /TransformParams <<'; 17482 $out .= ' /Type /TransformParams'; 17483 $out .= ' /P '.$this->signature_data['cert_type']; 17484 $out .= ' /V /1.2'; 17485 } else { 17486 $out .= ' /TransformMethod /UR3'; 17487 $out .= ' /TransformParams <<'; 17488 $out .= ' /Type /TransformParams'; 17489 $out .= ' /V /2.2'; 17490 if (!$this->empty_string($this->ur['document'])) { 17491 $out .= ' /Document['.$this->ur['document'].']'; 17492 } 17493 if (!$this->empty_string($this->ur['form'])) { 17494 $out .= ' /Form['.$this->ur['form'].']'; 17495 } 17496 if (!$this->empty_string($this->ur['signature'])) { 17497 $out .= ' /Signature['.$this->ur['signature'].']'; 17498 } 17499 if (!$this->empty_string($this->ur['annots'])) { 17500 $out .= ' /Annots['.$this->ur['annots'].']'; 17501 } 17502 if (!$this->empty_string($this->ur['ef'])) { 17503 $out .= ' /EF['.$this->ur['ef'].']'; 17504 } 17505 if (!$this->empty_string($this->ur['formex'])) { 17506 $out .= ' /FormEX['.$this->ur['formex'].']'; 17507 } 17508 } 17509 $out .= ' >>'; // close TransformParams 17510 // optional digest data (values must be calculated and replaced later) 17511 //$out .= ' /Data ********** 0 R'; 17512 //$out .= ' /DigestMethod/MD5'; 17513 //$out .= ' /DigestLocation[********** 34]'; 17514 //$out .= ' /DigestValue<********************************>'; 17515 $out .= ' >>'; 17516 $out .= ' ]'; // end of reference 17517 if (isset($this->signature_data['info']['Name']) AND !$this->empty_string($this->signature_data['info']['Name'])) { 17518 $out .= ' /Name '.$this->_textstring($this->signature_data['info']['Name'], $sigobjid); 17519 } 17520 if (isset($this->signature_data['info']['Location']) AND !$this->empty_string($this->signature_data['info']['Location'])) { 17521 $out .= ' /Location '.$this->_textstring($this->signature_data['info']['Location'], $sigobjid); 17522 } 17523 if (isset($this->signature_data['info']['Reason']) AND !$this->empty_string($this->signature_data['info']['Reason'])) { 17524 $out .= ' /Reason '.$this->_textstring($this->signature_data['info']['Reason'], $sigobjid); 17525 } 17526 if (isset($this->signature_data['info']['ContactInfo']) AND !$this->empty_string($this->signature_data['info']['ContactInfo'])) { 17527 $out .= ' /ContactInfo '.$this->_textstring($this->signature_data['info']['ContactInfo'], $sigobjid); 17528 } 17529 $out .= ' /M '.$this->_datestring($sigobjid); 17530 $out .= ' >>'; 17531 $out .= "\n".'endobj'; 17532 $this->_out($out); 17533 } 17534 17552 public function setUserRights( 17553 $enable=true, 17554 $document='/FullSave', 17555 $annots='/Create/Delete/Modify/Copy/Import/Export', 17556 $form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate', 17557 $signature='/Modify', 17558 $ef='/Create/Delete/Modify/Import', 17559 $formex='') { 17560 $this->ur['enabled'] = $enable; 17561 $this->ur['document'] = $document; 17562 $this->ur['annots'] = $annots; 17563 $this->ur['form'] = $form; 17564 $this->ur['signature'] = $signature; 17565 $this->ur['ef'] = $ef; 17566 $this->ur['formex'] = $formex; 17567 if (!$this->sign) { 17568 $this->setSignature('', '', '', '', 0, array()); 17569 } 17570 } 17571 17588 public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array()) { 17589 // to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt 17590 // to export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12 17591 // to convert pfx certificate to pem: openssl 17592 // OpenSSL> pkcs12 -in <cert.pfx> -out <cert.crt> -nodes 17593 $this->sign = true; 17594 ++$this->n; 17595 $this->sig_obj_id = $this->n; // signature widget 17596 ++$this->n; // signature object ($this->sig_obj_id + 1) 17597 $this->signature_data = array(); 17598 if (strlen($signing_cert) == 0) { 17599 $signing_cert = 'file://'.dirname(__FILE__).'/tcpdf.crt'; 17600 $private_key_password = 'tcpdfdemo'; 17601 } 17602 if (strlen($private_key) == 0) { 17603 $private_key = $signing_cert; 17604 } 17605 $this->signature_data['signcert'] = $signing_cert; 17606 $this->signature_data['privkey'] = $private_key; 17607 $this->signature_data['password'] = $private_key_password; 17608 $this->signature_data['extracerts'] = $extracerts; 17609 $this->signature_data['cert_type'] = $cert_type; 17610 $this->signature_data['info'] = $info; 17611 } 17612 17624 public function setSignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1) { 17625 $this->signature_appearance = $this->getSignatureAppearanceArray($x, $y, $w, $h, $page); 17626 } 17627 17639 public function addEmptySignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1) { 17640 ++$this->n; 17641 $this->empty_signature_appearance[] = array('objid' => $this->n) + $this->getSignatureAppearanceArray($x, $y, $w, $h, $page); 17642 } 17643 17656 protected function getSignatureAppearanceArray($x=0, $y=0, $w=0, $h=0, $page=-1) { 17657 $sigapp = array(); 17658 if (($page < 1) OR ($page > $this->numpages)) { 17659 $sigapp['page'] = $this->page; 17660 } else { 17661 $sigapp['page'] = intval($page); 17662 } 17663 $a = $x * $this->k; 17664 $b = $this->pagedim[($sigapp['page'])]['h'] - (($y + $h) * $this->k); 17665 $c = $w * $this->k; 17666 $d = $h * $this->k; 17667 $sigapp['rect'] = sprintf('%.2F %.2F %.2F %.2F', $a, $b, ($a + $c), ($b + $d)); 17668 return $sigapp; 17669 } 17670 17678 public function startPageGroup($page='') { 17679 if (empty($page)) { 17680 $page = $this->page + 1; 17681 } 17682 $this->newpagegroup[$page] = sizeof($this->newpagegroup) + 1; 17683 } 17684 17692 public function AliasNbPages($s='') {} 17693 17701 public function AliasNumPage($s='') {} 17702 17709 public function setStartingPageNumber($num=1) { 17710 $this->starting_page_number = max(0, intval($num)); 17711 } 17712 17720 public function getAliasRightShift() { 17721 // calculate aproximatively the ratio between widths of aliases and replacements. 17722 $ref = '{'.$this->alias_right_shift.'}{'.$this->alias_tot_pages.'}{'.$this->alias_num_page.'}'; 17723 $rep = str_repeat(' ', $this->GetNumChars($ref)); 17724 $wdiff = max(1, ($this->GetStringWidth($ref) / $this->GetStringWidth($rep))); 17725 $sdiff = sprintf('%.3F', $wdiff); 17726 $alias = $this->alias_right_shift.$sdiff.'}'; 17727 if ($this->isUnicodeFont()) { 17728 $alias = '{'.$alias; 17729 } 17730 return $alias; 17731 } 17732 17741 public function getAliasNbPages() { 17742 if ($this->isUnicodeFont()) { 17743 return '{'.$this->alias_tot_pages.'}'; 17744 } 17745 return $this->alias_tot_pages; 17746 } 17747 17756 public function getAliasNumPage() { 17757 if ($this->isUnicodeFont()) { 17758 return '{'.$this->alias_num_page.'}'; 17759 } 17760 return $this->alias_num_page; 17761 } 17762 17771 public function getPageGroupAlias() { 17772 if ($this->isUnicodeFont()) { 17773 return '{'.$this->alias_group_tot_pages.'}'; 17774 } 17775 return $this->alias_group_tot_pages; 17776 } 17777 17786 public function getPageNumGroupAlias() { 17787 if ($this->isUnicodeFont()) { 17788 return '{'.$this->alias_group_num_page.'}'; 17789 } 17790 return $this->alias_group_num_page; 17791 } 17792 17799 public function getGroupPageNo() { 17800 return $this->pagegroups[$this->currpagegroup]; 17801 } 17802 17809 public function getGroupPageNoFormatted() { 17810 return $this->formatPageNumber($this->getGroupPageNo()); 17811 } 17812 17820 protected function formatPageNumber($num) { 17821 return number_format((float)$num, 0, '', '.'); 17822 } 17823 17832 protected function formatTOCPageNumber($num) { 17833 return number_format((float)$num, 0, '', '.'); 17834 } 17835 17842 public function PageNoFormatted() { 17843 return $this->formatPageNumber($this->PageNo()); 17844 } 17845 17851 protected function _putocg() { 17852 if (empty($this->pdflayers)) { 17853 return; 17854 } 17855 foreach ($this->pdflayers as $key => $layer) { 17856 $this->pdflayers[$key]['objid'] = $this->_newobj(); 17857 $out = '<< /Type /OCG'; 17858 $out .= ' /Name '.$this->_textstring($layer['name'], $this->pdflayers[$key]['objid']); 17859 $out .= ' /Usage <<'; 17860 $out .= ' /Print <</PrintState /'.($layer['print']?'ON':'OFF').'>>'; 17861 $out .= ' /View <</ViewState /'.($layer['view']?'ON':'OFF').'>>'; 17862 $out .= ' >> >>'; 17863 $out .= "\n".'endobj'; 17864 $this->_out($out); 17865 } 17866 } 17867 17876 public function startLayer($name='', $print=true, $view=true) { 17877 $layer = sprintf('LYR%03d', (count($this->pdflayers) + 1)); 17878 if (empty($name)) { 17879 $name = $layer; 17880 } else { 17881 $name = preg_replace('/[^a-zA-Z0-9_\-]/', '', $name); 17882 } 17883 $this->pdflayers[] = array('layer' => $layer, 'name' => $name, 'print' => $print, 'view' => $view); 17884 $this->openMarkedContent = true; 17885 $this->_out('/OC /'.$layer.' BDC'); 17886 } 17887 17893 public function endLayer() { 17894 if ($this->openMarkedContent) { 17895 // close existing open marked-content layer 17896 $this->_out('EMC'); 17897 $this->openMarkedContent = false; 17898 } 17899 } 17900 17909 public function setVisibility($v) { 17910 $this->endLayer(); 17911 switch($v) { 17912 case 'print': { 17913 $this->startLayer('Print', true, false); 17914 break; 17915 } 17916 case 'view': 17917 case 'screen': { 17918 $this->startLayer('View', false, true); 17919 break; 17920 } 17921 case 'all': { 17922 $this->_out(''); 17923 break; 17924 } 17925 default: { 17926 $this->Error('Incorrect visibility: '.$v); 17927 break; 17928 } 17929 } 17930 } 17931 17939 protected function addExtGState($parms) { 17940 if ($this->pdfa_mode) { 17941 // transparencies are not allowed in PDF/A mode 17942 return; 17943 } 17944 // check if this ExtGState already exist 17945 foreach ($this->extgstates as $i => $ext) { 17946 if ($ext['parms'] == $parms) { 17947 if ($this->inxobj) { 17948 // we are inside an XObject template 17949 $this->xobjects[$this->xobjid]['extgstates'][$i] = $ext; 17950 } 17951 // return reference to existing ExtGState 17952 return $i; 17953 } 17954 } 17955 $n = (count($this->extgstates) + 1); 17956 $this->extgstates[$n] = array('parms' => $parms); 17957 if ($this->inxobj) { 17958 // we are inside an XObject template 17959 $this->xobjects[$this->xobjid]['extgstates'][$n] = $this->extgstates[$n]; 17960 } 17961 return $n; 17962 } 17963 17970 protected function setExtGState($gs) { 17971 if ($this->pdfa_mode) { 17972 // transparency is not allowed in PDF/A mode 17973 return; 17974 } 17975 $this->_out(sprintf('/GS%d gs', $gs)); 17976 } 17977 17983 protected function _putextgstates() { 17984 if ($this->pdfa_mode) { 17985 // transparencies are not allowed in PDF/A mode 17986 return; 17987 } 17988 foreach ($this->extgstates as $i => $ext) { 17989 $this->extgstates[$i]['n'] = $this->_newobj(); 17990 $out = '<< /Type /ExtGState'; 17991 foreach ($ext['parms'] as $k => $v) { 17992 if (is_float($v)) { 17993 $v = sprintf('%.2F', $v); 17994 } 17995 $out .= ' /'.$k.' '.$v; 17996 } 17997 $out .= ' >>'; 17998 $out .= "\n".'endobj'; 17999 $this->_out($out); 18000 } 18001 } 18002 18010 public function setAlpha($alpha, $bm='Normal') { 18011 if ($this->pdfa_mode) { 18012 // transparency is not allowed in PDF/A mode 18013 return; 18014 } 18015 $gs = $this->addExtGState(array('ca' => $alpha, 'CA' => $alpha, 'BM' => '/'.$bm, 'AIS' => 'false')); 18016 $this->setExtGState($gs); 18017 } 18018 18025 public function setJPEGQuality($quality) { 18026 if (($quality < 1) OR ($quality > 100)) { 18027 $quality = 75; 18028 } 18029 $this->jpeg_quality = intval($quality); 18030 } 18031 18038 public function setDefaultTableColumns($cols=4) { 18039 $this->default_table_columns = intval($cols); 18040 } 18041 18048 public function setCellHeightRatio($h) { 18049 $this->cell_height_ratio = $h; 18050 } 18051 18057 public function getCellHeightRatio() { 18058 return $this->cell_height_ratio; 18059 } 18060 18067 public function setPDFVersion($version='1.7') { 18068 if ($this->pdfa_mode) { 18069 // PDF/A mode 18070 $this->PDFVersion = '1.4'; 18071 } else { 18072 $this->PDFVersion = $version; 18073 } 18074 } 18075 18085 public function setViewerPreferences($preferences) { 18086 $this->viewer_preferences = $preferences; 18087 } 18088 18102 public function colorRegistrationBar($x, $y, $w, $h, $transition=true, $vertical=false, $colors='A,R,G,B,C,M,Y,K') { 18103 $bars = explode(',', $colors); 18104 $numbars = count($bars); // number of bars to print 18105 // set bar measures 18106 if ($vertical) { 18107 $coords = array(0, 0, 0, 1); 18108 $wb = $w / $numbars; // bar width 18109 $hb = $h; // bar height 18110 $xd = $wb; // delta x 18111 $yd = 0; // delta y 18112 } else { 18113 $coords = array(1, 0, 0, 0); 18114 $wb = $w; // bar width 18115 $hb = $h / $numbars; // bar height 18116 $xd = 0; // delta x 18117 $yd = $hb; // delta y 18118 } 18119 $xb = $x; 18120 $yb = $y; 18121 foreach ($bars as $col) { 18122 switch ($col) { 18123 // set transition colors 18124 case 'A': { // BLACK 18125 $col_a = array(255); 18126 $col_b = array(0); 18127 break; 18128 } 18129 case 'W': { // WHITE 18130 $col_a = array(0); 18131 $col_b = array(255); 18132 break; 18133 } 18134 case 'R': { // R 18135 $col_a = array(255,255,255); 18136 $col_b = array(255,0,0); 18137 break; 18138 } 18139 case 'G': { // G 18140 $col_a = array(255,255,255); 18141 $col_b = array(0,255,0); 18142 break; 18143 } 18144 case 'B': { // B 18145 $col_a = array(255,255,255); 18146 $col_b = array(0,0,255); 18147 break; 18148 } 18149 case 'C': { // C 18150 $col_a = array(0,0,0,0); 18151 $col_b = array(100,0,0,0); 18152 break; 18153 } 18154 case 'M': { // M 18155 $col_a = array(0,0,0,0); 18156 $col_b = array(0,100,0,0); 18157 break; 18158 } 18159 case 'Y': { // Y 18160 $col_a = array(0,0,0,0); 18161 $col_b = array(0,0,100,0); 18162 break; 18163 } 18164 case 'K': { // K 18165 $col_a = array(0,0,0,0); 18166 $col_b = array(0,0,0,100); 18167 break; 18168 } 18169 default: { // GRAY 18170 $col_a = array(255); 18171 $col_b = array(0); 18172 break; 18173 } 18174 } 18175 if ($transition) { 18176 // color gradient 18177 $this->LinearGradient($xb, $yb, $wb, $hb, $col_a, $col_b, $coords); 18178 } else { 18179 // color rectangle 18180 $this->SetFillColorArray($col_b); 18181 $this->Rect($xb, $yb, $wb, $hb, 'F', array()); 18182 } 18183 $xb += $xd; 18184 $yb += $yd; 18185 } 18186 } 18187 18200 public function cropMark($x, $y, $w, $h, $type='A,B,C,D', $color=array(0,0,0)) { 18201 $this->SetLineStyle(array('width' => (0.5 / $this->k), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $color)); 18202 $crops = explode(',', $type); 18203 $numcrops = count($crops); // number of crop marks to print 18204 $dw = $w / 4; // horizontal space to leave before the intersection point 18205 $dh = $h / 4; // vertical space to leave before the intersection point 18206 foreach ($crops as $crop) { 18207 switch ($crop) { 18208 case 'A': { 18209 $x1 = $x; 18210 $y1 = $y - $h; 18211 $x2 = $x; 18212 $y2 = $y - $dh; 18213 $x3 = $x - $w; 18214 $y3 = $y; 18215 $x4 = $x - $dw; 18216 $y4 = $y; 18217 break; 18218 } 18219 case 'B': { 18220 $x1 = $x; 18221 $y1 = $y - $h; 18222 $x2 = $x; 18223 $y2 = $y - $dh; 18224 $x3 = $x + $dw; 18225 $y3 = $y; 18226 $x4 = $x + $w; 18227 $y4 = $y; 18228 break; 18229 } 18230 case 'C': { 18231 $x1 = $x - $w; 18232 $y1 = $y; 18233 $x2 = $x - $dw; 18234 $y2 = $y; 18235 $x3 = $x; 18236 $y3 = $y + $dh; 18237 $x4 = $x; 18238 $y4 = $y + $h; 18239 break; 18240 } 18241 case 'D': { 18242 $x1 = $x + $dw; 18243 $y1 = $y; 18244 $x2 = $x + $w; 18245 $y2 = $y; 18246 $x3 = $x; 18247 $y3 = $y + $dh; 18248 $x4 = $x; 18249 $y4 = $y + $h; 18250 break; 18251 } 18252 } 18253 $this->Line($x1, $y1, $x2, $y2); 18254 $this->Line($x3, $y3, $x4, $y4); 18255 } 18256 } 18257 18270 public function registrationMark($x, $y, $r, $double=false, $cola=array(0,0,0), $colb=array(255,255,255)) { 18271 $line_style = array('width' => (0.5 / $this->k), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $cola); 18272 $this->SetFillColorArray($cola); 18273 $this->PieSector($x, $y, $r, 90, 180, 'F'); 18274 $this->PieSector($x, $y, $r, 270, 360, 'F'); 18275 $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8); 18276 if ($double) { 18277 $r2 = $r * 0.5; 18278 $this->SetFillColorArray($colb); 18279 $this->PieSector($x, $y, $r2, 90, 180, 'F'); 18280 $this->PieSector($x, $y, $r2, 270, 360, 'F'); 18281 $this->SetFillColorArray($cola); 18282 $this->PieSector($x, $y, $r2, 0, 90, 'F'); 18283 $this->PieSector($x, $y, $r2, 180, 270, 'F'); 18284 $this->Circle($x, $y, $r2, 0, 360, 'C', $line_style, array(), 8); 18285 } 18286 } 18287 18301 public function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) { 18302 $this->Clip($x, $y, $w, $h); 18303 $this->Gradient(2, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false); 18304 } 18305 18319 public function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) { 18320 $this->Clip($x, $y, $w, $h); 18321 $this->Gradient(3, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false); 18322 } 18323 18342 public function CoonsPatchMesh($x, $y, $w, $h, $col1=array(), $col2=array(), $col3=array(), $col4=array(), $coords=array(0.00,0.0,0.33,0.00,0.67,0.00,1.00,0.00,1.00,0.33,1.00,0.67,1.00,1.00,0.67,1.00,0.33,1.00,0.00,1.00,0.00,0.67,0.00,0.33), $coords_min=0, $coords_max=1, $antialias=false) { 18343 if ($this->pdfa_mode) { 18344 return; 18345 } 18346 $this->Clip($x, $y, $w, $h); 18347 $n = count($this->gradients) + 1; 18348 $this->gradients[$n] = array(); 18349 $this->gradients[$n]['type'] = 6; //coons patch mesh 18350 $this->gradients[$n]['coords'] = array(); 18351 $this->gradients[$n]['antialias'] = $antialias; 18352 $this->gradients[$n]['colors'] = array(); 18353 $this->gradients[$n]['transparency'] = false; 18354 //check the coords array if it is the simple array or the multi patch array 18355 if (!isset($coords[0]['f'])) { 18356 //simple array -> convert to multi patch array 18357 if (!isset($col1[1])) { 18358 $col1[1] = $col1[2] = $col1[0]; 18359 } 18360 if (!isset($col2[1])) { 18361 $col2[1] = $col2[2] = $col2[0]; 18362 } 18363 if (!isset($col3[1])) { 18364 $col3[1] = $col3[2] = $col3[0]; 18365 } 18366 if (!isset($col4[1])) { 18367 $col4[1] = $col4[2] = $col4[0]; 18368 } 18369 $patch_array[0]['f'] = 0; 18370 $patch_array[0]['points'] = $coords; 18371 $patch_array[0]['colors'][0]['r'] = $col1[0]; 18372 $patch_array[0]['colors'][0]['g'] = $col1[1]; 18373 $patch_array[0]['colors'][0]['b'] = $col1[2]; 18374 $patch_array[0]['colors'][1]['r'] = $col2[0]; 18375 $patch_array[0]['colors'][1]['g'] = $col2[1]; 18376 $patch_array[0]['colors'][1]['b'] = $col2[2]; 18377 $patch_array[0]['colors'][2]['r'] = $col3[0]; 18378 $patch_array[0]['colors'][2]['g'] = $col3[1]; 18379 $patch_array[0]['colors'][2]['b'] = $col3[2]; 18380 $patch_array[0]['colors'][3]['r'] = $col4[0]; 18381 $patch_array[0]['colors'][3]['g'] = $col4[1]; 18382 $patch_array[0]['colors'][3]['b'] = $col4[2]; 18383 } else { 18384 //multi patch array 18385 $patch_array = $coords; 18386 } 18387 $bpcd = 65535; //16 bits per coordinate 18388 //build the data stream 18389 $this->gradients[$n]['stream'] = ''; 18390 $count_patch = count($patch_array); 18391 for ($i=0; $i < $count_patch; ++$i) { 18392 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit 18393 $count_points = count($patch_array[$i]['points']); 18394 for ($j=0; $j < $count_points; ++$j) { 18395 //each point as 16 bit 18396 $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j] - $coords_min) / ($coords_max - $coords_min)) * $bpcd; 18397 if ($patch_array[$i]['points'][$j] < 0) { 18398 $patch_array[$i]['points'][$j] = 0; 18399 } 18400 if ($patch_array[$i]['points'][$j] > $bpcd) { 18401 $patch_array[$i]['points'][$j] = $bpcd; 18402 } 18403 $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] / 256)); 18404 $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] % 256)); 18405 } 18406 $count_cols = count($patch_array[$i]['colors']); 18407 for ($j=0; $j < $count_cols; ++$j) { 18408 //each color component as 8 bit 18409 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']); 18410 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']); 18411 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']); 18412 } 18413 } 18414 //paint the gradient 18415 $this->_out('/Sh'.$n.' sh'); 18416 //restore previous Graphic State 18417 $this->_out('Q'); 18418 if ($this->inxobj) { 18419 // we are inside an XObject template 18420 $this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n]; 18421 } 18422 } 18423 18434 protected function Clip($x, $y, $w, $h) { 18435 if ($this->rtl) { 18436 $x = $this->w - $x - $w; 18437 } 18438 //save current Graphic State 18439 $s = 'q'; 18440 //set clipping area 18441 $s .= sprintf(' %.2F %.2F %.2F %.2F re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k); 18442 //set up transformation matrix for gradient 18443 $s .= sprintf(' %.3F 0 0 %.3F %.3F %.3F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k); 18444 $this->_out($s); 18445 } 18446 18458 public function Gradient($type, $coords, $stops, $background=array(), $antialias=false) { 18459 if ($this->pdfa_mode) { 18460 return; 18461 } 18462 $n = count($this->gradients) + 1; 18463 $this->gradients[$n] = array(); 18464 $this->gradients[$n]['type'] = $type; 18465 $this->gradients[$n]['coords'] = $coords; 18466 $this->gradients[$n]['antialias'] = $antialias; 18467 $this->gradients[$n]['colors'] = array(); 18468 $this->gradients[$n]['transparency'] = false; 18469 // color space 18470 $numcolspace = count($stops[0]['color']); 18471 $bcolor = array_values($background); 18472 switch($numcolspace) { 18473 case 4: { // CMYK 18474 $this->gradients[$n]['colspace'] = 'DeviceCMYK'; 18475 if (!empty($background)) { 18476 $this->gradients[$n]['background'] = sprintf('%.3F %.3F %.3F %.3F', $bcolor[0]/100, $bcolor[1]/100, $bcolor[2]/100, $bcolor[3]/100); 18477 } 18478 break; 18479 } 18480 case 3: { // RGB 18481 $this->gradients[$n]['colspace'] = 'DeviceRGB'; 18482 if (!empty($background)) { 18483 $this->gradients[$n]['background'] = sprintf('%.3F %.3F %.3F', $bcolor[0]/255, $bcolor[1]/255, $bcolor[2]/255); 18484 } 18485 break; 18486 } 18487 case 1: { // Gray scale 18488 $this->gradients[$n]['colspace'] = 'DeviceGray'; 18489 if (!empty($background)) { 18490 $this->gradients[$n]['background'] = sprintf('%.3F', $bcolor[0]/255); 18491 } 18492 break; 18493 } 18494 } 18495 $num_stops = count($stops); 18496 $last_stop_id = $num_stops - 1; 18497 foreach ($stops as $key => $stop) { 18498 $this->gradients[$n]['colors'][$key] = array(); 18499 // offset represents a location along the gradient vector 18500 if (isset($stop['offset'])) { 18501 $this->gradients[$n]['colors'][$key]['offset'] = $stop['offset']; 18502 } else { 18503 if ($key == 0) { 18504 $this->gradients[$n]['colors'][$key]['offset'] = 0; 18505 } elseif ($key == $last_stop_id) { 18506 $this->gradients[$n]['colors'][$key]['offset'] = 1; 18507 } else { 18508 $offsetstep = (1 - $this->gradients[$n]['colors'][($key - 1)]['offset']) / ($num_stops - $key); 18509 $this->gradients[$n]['colors'][$key]['offset'] = $this->gradients[$n]['colors'][($key - 1)]['offset'] + $offsetstep; 18510 } 18511 } 18512 if (isset($stop['opacity'])) { 18513 $this->gradients[$n]['colors'][$key]['opacity'] = $stop['opacity']; 18514 if ((!$this->pdfa_mode) AND ($stop['opacity'] < 1)) { 18515 $this->gradients[$n]['transparency'] = true; 18516 } 18517 } else { 18518 $this->gradients[$n]['colors'][$key]['opacity'] = 1; 18519 } 18520 // exponent for the exponential interpolation function 18521 if (isset($stop['exponent'])) { 18522 $this->gradients[$n]['colors'][$key]['exponent'] = $stop['exponent']; 18523 } else { 18524 $this->gradients[$n]['colors'][$key]['exponent'] = 1; 18525 } 18526 // set colors 18527 $color = array_values($stop['color']); 18528 switch($numcolspace) { 18529 case 4: { // CMYK 18530 $this->gradients[$n]['colors'][$key]['color'] = sprintf('%.3F %.3F %.3F %.3F', $color[0]/100, $color[1]/100, $color[2]/100, $color[3]/100); 18531 break; 18532 } 18533 case 3: { // RGB 18534 $this->gradients[$n]['colors'][$key]['color'] = sprintf('%.3F %.3F %.3F', $color[0]/255, $color[1]/255, $color[2]/255); 18535 break; 18536 } 18537 case 1: { // Gray scale 18538 $this->gradients[$n]['colors'][$key]['color'] = sprintf('%.3F', $color[0]/255); 18539 break; 18540 } 18541 } 18542 } 18543 if ($this->gradients[$n]['transparency']) { 18544 // paint luminosity gradient 18545 $this->_out('/TGS'.$n.' gs'); 18546 } 18547 //paint the gradient 18548 $this->_out('/Sh'.$n.' sh'); 18549 //restore previous Graphic State 18550 $this->_out('Q'); 18551 if ($this->inxobj) { 18552 // we are inside an XObject template 18553 $this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n]; 18554 } 18555 } 18556 18563 function _putshaders() { 18564 if ($this->pdfa_mode) { 18565 return; 18566 } 18567 $idt = count($this->gradients); //index for transparency gradients 18568 foreach ($this->gradients as $id => $grad) { 18569 if (($grad['type'] == 2) OR ($grad['type'] == 3)) { 18570 $fc = $this->_newobj(); 18571 $out = '<<'; 18572 $out .= ' /FunctionType 3'; 18573 $out .= ' /Domain [0 1]'; 18574 $functions = ''; 18575 $bounds = ''; 18576 $encode = ''; 18577 $i = 1; 18578 $num_cols = count($grad['colors']); 18579 $lastcols = $num_cols - 1; 18580 for ($i = 1; $i < $num_cols; ++$i) { 18581 $functions .= ($fc + $i).' 0 R '; 18582 if ($i < $lastcols) { 18583 $bounds .= sprintf('%.3F ', $grad['colors'][$i]['offset']); 18584 } 18585 $encode .= '0 1 '; 18586 } 18587 $out .= ' /Functions ['.trim($functions).']'; 18588 $out .= ' /Bounds ['.trim($bounds).']'; 18589 $out .= ' /Encode ['.trim($encode).']'; 18590 $out .= ' >>'; 18591 $out .= "\n".'endobj'; 18592 $this->_out($out); 18593 for ($i = 1; $i < $num_cols; ++$i) { 18594 $this->_newobj(); 18595 $out = '<<'; 18596 $out .= ' /FunctionType 2'; 18597 $out .= ' /Domain [0 1]'; 18598 $out .= ' /C0 ['.$grad['colors'][($i - 1)]['color'].']'; 18599 $out .= ' /C1 ['.$grad['colors'][$i]['color'].']'; 18600 $out .= ' /N '.$grad['colors'][$i]['exponent']; 18601 $out .= ' >>'; 18602 $out .= "\n".'endobj'; 18603 $this->_out($out); 18604 } 18605 // set transparency fuctions 18606 if ($grad['transparency']) { 18607 $ft = $this->_newobj(); 18608 $out = '<<'; 18609 $out .= ' /FunctionType 3'; 18610 $out .= ' /Domain [0 1]'; 18611 $functions = ''; 18612 $i = 1; 18613 $num_cols = count($grad['colors']); 18614 for ($i = 1; $i < $num_cols; ++$i) { 18615 $functions .= ($ft + $i).' 0 R '; 18616 } 18617 $out .= ' /Functions ['.trim($functions).']'; 18618 $out .= ' /Bounds ['.trim($bounds).']'; 18619 $out .= ' /Encode ['.trim($encode).']'; 18620 $out .= ' >>'; 18621 $out .= "\n".'endobj'; 18622 $this->_out($out); 18623 for ($i = 1; $i < $num_cols; ++$i) { 18624 $this->_newobj(); 18625 $out = '<<'; 18626 $out .= ' /FunctionType 2'; 18627 $out .= ' /Domain [0 1]'; 18628 $out .= ' /C0 ['.$grad['colors'][($i - 1)]['opacity'].']'; 18629 $out .= ' /C1 ['.$grad['colors'][$i]['opacity'].']'; 18630 $out .= ' /N '.$grad['colors'][$i]['exponent']; 18631 $out .= ' >>'; 18632 $out .= "\n".'endobj'; 18633 $this->_out($out); 18634 } 18635 } 18636 } 18637 // set shading object 18638 $this->_newobj(); 18639 $out = '<< /ShadingType '.$grad['type']; 18640 if (isset($grad['colspace'])) { 18641 $out .= ' /ColorSpace /'.$grad['colspace']; 18642 } else { 18643 $out .= ' /ColorSpace /DeviceRGB'; 18644 } 18645 if (isset($grad['background']) AND !empty($grad['background'])) { 18646 $out .= ' /Background ['.$grad['background'].']'; 18647 } 18648 if (isset($grad['antialias']) AND ($grad['antialias'] === true)) { 18649 $out .= ' /AntiAlias true'; 18650 } 18651 if ($grad['type'] == 2) { 18652 $out .= ' '.sprintf('/Coords [%.3F %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]); 18653 $out .= ' /Domain [0 1]'; 18654 $out .= ' /Function '.$fc.' 0 R'; 18655 $out .= ' /Extend [true true]'; 18656 $out .= ' >>'; 18657 } elseif ($grad['type'] == 3) { 18658 //x0, y0, r0, x1, y1, r1 18659 //at this this time radius of inner circle is 0 18660 $out .= ' '.sprintf('/Coords [%.3F %.3F 0 %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]); 18661 $out .= ' /Domain [0 1]'; 18662 $out .= ' /Function '.$fc.' 0 R'; 18663 $out .= ' /Extend [true true]'; 18664 $out .= ' >>'; 18665 } elseif ($grad['type'] == 6) { 18666 $out .= ' /BitsPerCoordinate 16'; 18667 $out .= ' /BitsPerComponent 8'; 18668 $out .= ' /Decode[0 1 0 1 0 1 0 1 0 1]'; 18669 $out .= ' /BitsPerFlag 8'; 18670 $stream = $this->_getrawstream($grad['stream']); 18671 $out .= ' /Length '.strlen($stream); 18672 $out .= ' >>'; 18673 $out .= ' stream'."\n".$stream."\n".'endstream'; 18674 } 18675 $out .= "\n".'endobj'; 18676 $this->_out($out); 18677 if ($grad['transparency']) { 18678 $shading_transparency = preg_replace('/\/ColorSpace \/[^\s]+/si', '/ColorSpace /DeviceGray', $out); 18679 $shading_transparency = preg_replace('/\/Function [0-9]+ /si', '/Function '.$ft.' ', $shading_transparency); 18680 } 18681 $this->gradients[$id]['id'] = $this->n; 18682 // set pattern object 18683 $this->_newobj(); 18684 $out = '<< /Type /Pattern /PatternType 2'; 18685 $out .= ' /Shading '.$this->gradients[$id]['id'].' 0 R'; 18686 $out .= ' >>'; 18687 $out .= "\n".'endobj'; 18688 $this->_out($out); 18689 $this->gradients[$id]['pattern'] = $this->n; 18690 // set shading and pattern for transparency mask 18691 if ($grad['transparency']) { 18692 // luminosity pattern 18693 $idgs = $id + $idt; 18694 $this->_newobj(); 18695 $this->_out($shading_transparency); 18696 $this->gradients[$idgs]['id'] = $this->n; 18697 $this->_newobj(); 18698 $out = '<< /Type /Pattern /PatternType 2'; 18699 $out .= ' /Shading '.$this->gradients[$idgs]['id'].' 0 R'; 18700 $out .= ' >>'; 18701 $out .= "\n".'endobj'; 18702 $this->_out($out); 18703 $this->gradients[$idgs]['pattern'] = $this->n; 18704 // luminosity XObject 18705 $oid = $this->_newobj(); 18706 $this->xobjects['LX'.$oid] = array('n' => $oid); 18707 $filter = ''; 18708 $stream = 'q /a0 gs /Pattern cs /p'.$idgs.' scn 0 0 '.$this->wPt.' '.$this->hPt.' re f Q'; 18709 if ($this->compress) { 18710 $filter = ' /Filter /FlateDecode'; 18711 $stream = gzcompress($stream); 18712 } 18713 $stream = $this->_getrawstream($stream); 18714 $out = '<< /Type /XObject /Subtype /Form /FormType 1'.$filter; 18715 $out .= ' /Length '.strlen($stream); 18716 $rect = sprintf('%.2F %.2F', $this->wPt, $this->hPt); 18717 $out .= ' /BBox [0 0 '.$rect.']'; 18718 $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceGray >>'; 18719 $out .= ' /Resources <<'; 18720 $out .= ' /ExtGState << /a0 << /ca 1 /CA 1 >> >>'; 18721 $out .= ' /Pattern << /p'.$idgs.' '.$this->gradients[$idgs]['pattern'].' 0 R >>'; 18722 $out .= ' >>'; 18723 $out .= ' >> '; 18724 $out .= ' stream'."\n".$stream."\n".'endstream'; 18725 $out .= "\n".'endobj'; 18726 $this->_out($out); 18727 // SMask 18728 $this->_newobj(); 18729 $out = '<< /Type /Mask /S /Luminosity /G '.($this->n - 1).' 0 R >>'."\n".'endobj'; 18730 $this->_out($out); 18731 // ExtGState 18732 $this->_newobj(); 18733 $out = '<< /Type /ExtGState /SMask '.($this->n - 1).' 0 R /AIS false >>'."\n".'endobj'; 18734 $this->_out($out); 18735 $this->extgstates[] = array('n' => $this->n, 'name' => 'TGS'.$id); 18736 } 18737 } 18738 } 18739 18755 public function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) { 18756 $this->PieSectorXY($xc, $yc, $r, $r, $a, $b, $style, $cw, $o); 18757 } 18758 18776 public function PieSectorXY($xc, $yc, $rx, $ry, $a, $b, $style='FD', $cw=false, $o=0, $nc=2) { 18777 if ($this->rtl) { 18778 $xc = $this->w - $xc; 18779 } 18780 $op = $this->getPathPaintOperator($style); 18781 if ($op == 'f') { 18782 $line_style = array(); 18783 } 18784 if ($cw) { 18785 $d = $b; 18786 $b = 360 - $a + $o; 18787 $a = 360 - $d + $o; 18788 } else { 18789 $b += $o; 18790 $a += $o; 18791 } 18792 $this->_outellipticalarc($xc, $yc, $rx, $ry, 0, $a, $b, true, $nc); 18793 $this->_out($op); 18794 } 18795 18817 public function ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0, $fitonpage=false, $fixoutvals=false) { 18818 if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) { 18819 // convert EPS to raster image using GD or ImageMagick libraries 18820 return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage); 18821 } 18822 if ($x === '') { 18823 $x = $this->x; 18824 } 18825 if ($y === '') { 18826 $y = $this->y; 18827 } 18828 // check page for no-write regions and adapt page margins if necessary 18829 list($x, $y) = $this->checkPageRegions($h, $x, $y); 18830 $k = $this->k; 18831 if ($file{0} === '@') { // image from string 18832 $data = substr($file, 1); 18833 } else { // EPS/AI file 18834 $data = file_get_contents($file); 18835 } 18836 if ($data === false) { 18837 $this->Error('EPS file not found: '.$file); 18838 } 18839 $regs = array(); 18840 // EPS/AI compatibility check (only checks files created by Adobe Illustrator!) 18841 preg_match("/%%Creator:([^\r\n]+)/", $data, $regs); # find Creator 18842 if (count($regs) > 1) { 18843 $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0" 18844 if (strpos($version_str, 'Adobe Illustrator') !== false) { 18845 $versexp = explode(' ', $version_str); 18846 $version = (float)array_pop($versexp); 18847 if ($version >= 9) { 18848 $this->Error('This version of Adobe Illustrator file is not supported: '.$file); 18849 } 18850 } 18851 } 18852 // strip binary bytes in front of PS-header 18853 $start = strpos($data, '%!PS-Adobe'); 18854 if ($start > 0) { 18855 $data = substr($data, $start); 18856 } 18857 // find BoundingBox params 18858 preg_match("/%%BoundingBox:([^\r\n]+)/", $data, $regs); 18859 if (count($regs) > 1) { 18860 list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1])); 18861 } else { 18862 $this->Error('No BoundingBox found in EPS/AI file: '.$file); 18863 } 18864 $start = strpos($data, '%%EndSetup'); 18865 if ($start === false) { 18866 $start = strpos($data, '%%EndProlog'); 18867 } 18868 if ($start === false) { 18869 $start = strpos($data, '%%BoundingBox'); 18870 } 18871 $data = substr($data, $start); 18872 $end = strpos($data, '%%PageTrailer'); 18873 if ($end===false) { 18874 $end = strpos($data, 'showpage'); 18875 } 18876 if ($end) { 18877 $data = substr($data, 0, $end); 18878 } 18879 // calculate image width and height on document 18880 if (($w <= 0) AND ($h <= 0)) { 18881 $w = ($x2 - $x1) / $k; 18882 $h = ($y2 - $y1) / $k; 18883 } elseif ($w <= 0) { 18884 $w = ($x2-$x1) / $k * ($h / (($y2 - $y1) / $k)); 18885 } elseif ($h <= 0) { 18886 $h = ($y2 - $y1) / $k * ($w / (($x2 - $x1) / $k)); 18887 } 18888 // fit the image on available space 18889 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage); 18890 if ($this->rasterize_vector_images) { 18891 // convert EPS to raster image using GD or ImageMagick libraries 18892 return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage); 18893 } 18894 // set scaling factors 18895 $scale_x = $w / (($x2 - $x1) / $k); 18896 $scale_y = $h / (($y2 - $y1) / $k); 18897 // set alignment 18898 $this->img_rb_y = $y + $h; 18899 // set alignment 18900 if ($this->rtl) { 18901 if ($palign == 'L') { 18902 $ximg = $this->lMargin; 18903 } elseif ($palign == 'C') { 18904 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 18905 } elseif ($palign == 'R') { 18906 $ximg = $this->w - $this->rMargin - $w; 18907 } else { 18908 $ximg = $x - $w; 18909 } 18910 $this->img_rb_x = $ximg; 18911 } else { 18912 if ($palign == 'L') { 18913 $ximg = $this->lMargin; 18914 } elseif ($palign == 'C') { 18915 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 18916 } elseif ($palign == 'R') { 18917 $ximg = $this->w - $this->rMargin - $w; 18918 } else { 18919 $ximg = $x; 18920 } 18921 $this->img_rb_x = $ximg + $w; 18922 } 18923 if ($useBoundingBox) { 18924 $dx = $ximg * $k - $x1; 18925 $dy = $y * $k - $y1; 18926 } else { 18927 $dx = $ximg * $k; 18928 $dy = $y * $k; 18929 } 18930 // save the current graphic state 18931 $this->_out('q'.$this->epsmarker); 18932 // translate 18933 $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', 1, 0, 0, 1, $dx, $dy + ($this->hPt - (2 * $y * $k) - ($y2 - $y1)))); 18934 // scale 18935 if (isset($scale_x)) { 18936 $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $scale_x, 0, 0, $scale_y, $x1 * (1 - $scale_x), $y2 * (1 - $scale_y))); 18937 } 18938 // handle pc/unix/mac line endings 18939 $lines = preg_split('/[\r\n]+/si', $data, -1, PREG_SPLIT_NO_EMPTY); 18940 $u=0; 18941 $cnt = count($lines); 18942 for ($i=0; $i < $cnt; ++$i) { 18943 $line = $lines[$i]; 18944 if (($line == '') OR ($line{0} == '%')) { 18945 continue; 18946 } 18947 $len = strlen($line); 18948 // check for spot color names 18949 $color_name = ''; 18950 if (strcasecmp('x', substr(trim($line), -1)) == 0) { 18951 if (preg_match('/\([^\)]*\)/', $line, $matches) > 0) { 18952 // extract spot color name 18953 $color_name = $matches[0]; 18954 // remove color name from string 18955 $line = str_replace(' '.$color_name, '', $line); 18956 // remove pharentesis from color name 18957 $color_name = substr($color_name, 1, -1); 18958 } 18959 } 18960 $chunks = explode(' ', $line); 18961 $cmd = trim(array_pop($chunks)); 18962 // RGB 18963 if (($cmd == 'Xa') OR ($cmd == 'XA')) { 18964 $b = array_pop($chunks); 18965 $g = array_pop($chunks); 18966 $r = array_pop($chunks); 18967 $this->_out(''.$r.' '.$g.' '.$b.' '.($cmd=='Xa'?'rg':'RG')); //substr($line, 0, -2).'rg' -> in EPS (AI8): c m y k r g b rg! 18968 continue; 18969 } 18970 $skip = false; 18971 if ($fixoutvals) { 18972 // check for values outside the bounding box 18973 switch ($cmd) { 18974 case 'm': 18975 case 'l': 18976 case 'L': { 18977 // skip values outside bounding box 18978 foreach ($chunks as $key => $val) { 18979 if ((($key % 2) == 0) AND (($val < $x1) OR ($val > $x2))) { 18980 $skip = true; 18981 } elseif ((($key % 2) != 0) AND (($val < $y1) OR ($val > $y2))) { 18982 $skip = true; 18983 } 18984 } 18985 } 18986 } 18987 } 18988 switch ($cmd) { 18989 case 'm': 18990 case 'l': 18991 case 'v': 18992 case 'y': 18993 case 'c': 18994 case 'k': 18995 case 'K': 18996 case 'g': 18997 case 'G': 18998 case 's': 18999 case 'S': 19000 case 'J': 19001 case 'j': 19002 case 'w': 19003 case 'M': 19004 case 'd': 19005 case 'n': { 19006 if ($skip) { 19007 break; 19008 } 19009 $this->_out($line); 19010 break; 19011 } 19012 case 'x': {// custom fill color 19013 if (empty($color_name)) { 19014 // CMYK color 19015 list($col_c, $col_m, $col_y, $col_k) = $chunks; 19016 $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' k'); 19017 } else { 19018 // Spot Color (CMYK + tint) 19019 list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks; 19020 $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100)); 19021 $color_cmd = sprintf('/CS%d cs %.3F scn', $this->spot_colors[$color_name]['i'], (1 - $col_t)); 19022 $this->_out($color_cmd); 19023 } 19024 break; 19025 } 19026 case 'X': { // custom stroke color 19027 if (empty($color_name)) { 19028 // CMYK color 19029 list($col_c, $col_m, $col_y, $col_k) = $chunks; 19030 $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' K'); 19031 } else { 19032 // Spot Color (CMYK + tint) 19033 list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks; 19034 $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100)); 19035 $color_cmd = sprintf('/CS%d CS %.3F SCN', $this->spot_colors[$color_name]['i'], (1 - $col_t)); 19036 $this->_out($color_cmd); 19037 } 19038 break; 19039 } 19040 case 'Y': 19041 case 'N': 19042 case 'V': 19043 case 'L': 19044 case 'C': { 19045 if ($skip) { 19046 break; 19047 } 19048 $line{$len-1} = strtolower($cmd); 19049 $this->_out($line); 19050 break; 19051 } 19052 case 'b': 19053 case 'B': { 19054 $this->_out($cmd . '*'); 19055 break; 19056 } 19057 case 'f': 19058 case 'F': { 19059 if ($u > 0) { 19060 $isU = false; 19061 $max = min(($i + 5), $cnt); 19062 for ($j = ($i + 1); $j < $max; ++$j) { 19063 $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U'))); 19064 } 19065 if ($isU) { 19066 $this->_out('f*'); 19067 } 19068 } else { 19069 $this->_out('f*'); 19070 } 19071 break; 19072 } 19073 case '*u': { 19074 ++$u; 19075 break; 19076 } 19077 case '*U': { 19078 --$u; 19079 break; 19080 } 19081 } 19082 } 19083 // restore previous graphic state 19084 $this->_out($this->epsmarker.'Q'); 19085 if (!empty($border)) { 19086 $bx = $this->x; 19087 $by = $this->y; 19088 $this->x = $ximg; 19089 if ($this->rtl) { 19090 $this->x += $w; 19091 } 19092 $this->y = $y; 19093 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true); 19094 $this->x = $bx; 19095 $this->y = $by; 19096 } 19097 if ($link) { 19098 $this->Link($ximg, $y, $w, $h, $link, 0); 19099 } 19100 // set pointer to align the next text/objects 19101 switch($align) { 19102 case 'T':{ 19103 $this->y = $y; 19104 $this->x = $this->img_rb_x; 19105 break; 19106 } 19107 case 'M':{ 19108 $this->y = $y + round($h/2); 19109 $this->x = $this->img_rb_x; 19110 break; 19111 } 19112 case 'B':{ 19113 $this->y = $this->img_rb_y; 19114 $this->x = $this->img_rb_x; 19115 break; 19116 } 19117 case 'N':{ 19118 $this->SetY($this->img_rb_y); 19119 break; 19120 } 19121 default:{ 19122 break; 19123 } 19124 } 19125 $this->endlinex = $this->img_rb_x; 19126 } 19127 19133 public function setBarcode($bc='') { 19134 $this->barcode = $bc; 19135 } 19136 19143 public function getBarcode() { 19144 return $this->barcode; 19145 } 19146 19177 public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres='', $style='', $align='') { 19178 if ($this->empty_string(trim($code))) { 19179 return; 19180 } 19181 require_once(dirname(__FILE__).'/barcodes.php'); 19182 // save current graphic settings 19183 $gvars = $this->getGraphicVars(); 19184 // create new barcode object 19185 $barcodeobj = new TCPDFBarcode($code, $type); 19186 $arrcode = $barcodeobj->getBarcodeArray(); 19187 if ($arrcode === false) { 19188 $this->Error('Error in 1D barcode string'); 19189 } 19190 // set default values 19191 if (!isset($style['position'])) { 19192 $style['position'] = ''; 19193 } elseif ($style['position'] == 'S') { 19194 // keep this for backward compatibility 19195 $style['position'] = ''; 19196 $style['stretch'] = true; 19197 } 19198 if (!isset($style['fitwidth'])) { 19199 if (!isset($style['stretch'])) { 19200 $style['fitwidth'] = true; 19201 } else { 19202 $style['fitwidth'] = false; 19203 } 19204 } 19205 if ($style['fitwidth']) { 19206 // disable stretch 19207 $style['stretch'] = false; 19208 } 19209 if (!isset($style['stretch'])) { 19210 if (($w === '') OR ($w <= 0)) { 19211 $style['stretch'] = false; 19212 } else { 19213 $style['stretch'] = true; 19214 } 19215 } 19216 if (!isset($style['fgcolor'])) { 19217 $style['fgcolor'] = array(0,0,0); // default black 19218 } 19219 if (!isset($style['bgcolor'])) { 19220 $style['bgcolor'] = false; // default transparent 19221 } 19222 if (!isset($style['border'])) { 19223 $style['border'] = false; 19224 } 19225 $fontsize = 0; 19226 if (!isset($style['text'])) { 19227 $style['text'] = false; 19228 } 19229 if ($style['text'] AND isset($style['font'])) { 19230 if (isset($style['fontsize'])) { 19231 $fontsize = $style['fontsize']; 19232 } 19233 $this->SetFont($style['font'], '', $fontsize); 19234 } 19235 if (!isset($style['stretchtext'])) { 19236 $style['stretchtext'] = 4; 19237 } 19238 if ($x === '') { 19239 $x = $this->x; 19240 } 19241 if ($y === '') { 19242 $y = $this->y; 19243 } 19244 // check page for no-write regions and adapt page margins if necessary 19245 list($x, $y) = $this->checkPageRegions($h, $x, $y); 19246 if (($w === '') OR ($w <= 0)) { 19247 if ($this->rtl) { 19248 $w = $x - $this->lMargin; 19249 } else { 19250 $w = $this->w - $this->rMargin - $x; 19251 } 19252 } 19253 // padding 19254 if (!isset($style['padding'])) { 19255 $padding = 0; 19256 } elseif ($style['padding'] === 'auto') { 19257 $padding = 10 * ($w / ($arrcode['maxw'] + 20)); 19258 } else { 19259 $padding = floatval($style['padding']); 19260 } 19261 // horizontal padding 19262 if (!isset($style['hpadding'])) { 19263 $hpadding = $padding; 19264 } elseif ($style['hpadding'] === 'auto') { 19265 $hpadding = 10 * ($w / ($arrcode['maxw'] + 20)); 19266 } else { 19267 $hpadding = floatval($style['hpadding']); 19268 } 19269 // vertical padding 19270 if (!isset($style['vpadding'])) { 19271 $vpadding = $padding; 19272 } elseif ($style['vpadding'] === 'auto') { 19273 $vpadding = ($hpadding / 2); 19274 } else { 19275 $vpadding = floatval($style['vpadding']); 19276 } 19277 // calculate xres (single bar width) 19278 $max_xres = ($w - (2 * $hpadding)) / $arrcode['maxw']; 19279 if ($style['stretch']) { 19280 $xres = $max_xres; 19281 } else { 19282 if ($this->empty_string($xres)) { 19283 $xres = (0.141 * $this->k); // default bar width = 0.4 mm 19284 } 19285 if ($xres > $max_xres) { 19286 // correct xres to fit on $w 19287 $xres = $max_xres; 19288 } 19289 if ((isset($style['padding']) AND ($style['padding'] === 'auto')) 19290 OR (isset($style['hpadding']) AND ($style['hpadding'] === 'auto'))) { 19291 $hpadding = 10 * $xres; 19292 if (isset($style['vpadding']) AND ($style['vpadding'] === 'auto')) { 19293 $vpadding = ($hpadding / 2); 19294 } 19295 } 19296 } 19297 if ($style['fitwidth']) { 19298 $wold = $w; 19299 $w = (($arrcode['maxw'] * $xres) + (2 * $hpadding)); 19300 if (isset($style['cellfitalign'])) { 19301 switch ($style['cellfitalign']) { 19302 case 'L': { 19303 if ($this->rtl) { 19304 $x -= ($wold - $w); 19305 } 19306 break; 19307 } 19308 case 'R': { 19309 if (!$this->rtl) { 19310 $x += ($wold - $w); 19311 } 19312 break; 19313 } 19314 case 'C': { 19315 if ($this->rtl) { 19316 $x -= (($wold - $w) / 2); 19317 } else { 19318 $x += (($wold - $w) / 2); 19319 } 19320 break; 19321 } 19322 default : { 19323 break; 19324 } 19325 } 19326 } 19327 } 19328 $text_height = ($this->cell_height_ratio * $fontsize / $this->k); 19329 // height 19330 if (($h === '') OR ($h <= 0)) { 19331 // set default height 19332 $h = (($arrcode['maxw'] * $xres) / 3) + (2 * $vpadding) + $text_height; 19333 } 19334 $barh = $h - $text_height - (2 * $vpadding); 19335 if ($barh <=0) { 19336 // try to reduce font or padding to fit barcode on available height 19337 if ($text_height > $h) { 19338 $fontsize = (($h * $this->k) / (4 * $this->cell_height_ratio)); 19339 $text_height = ($this->cell_height_ratio * $fontsize / $this->k); 19340 $this->SetFont($style['font'], '', $fontsize); 19341 } 19342 if ($vpadding > 0) { 19343 $vpadding = (($h - $text_height) / 4); 19344 } 19345 $barh = $h - $text_height - (2 * $vpadding); 19346 } 19347 // fit the barcode on available space 19348 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false); 19349 // set alignment 19350 $this->img_rb_y = $y + $h; 19351 // set alignment 19352 if ($this->rtl) { 19353 if ($style['position'] == 'L') { 19354 $xpos = $this->lMargin; 19355 } elseif ($style['position'] == 'C') { 19356 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 19357 } elseif ($style['position'] == 'R') { 19358 $xpos = $this->w - $this->rMargin - $w; 19359 } else { 19360 $xpos = $x - $w; 19361 } 19362 $this->img_rb_x = $xpos; 19363 } else { 19364 if ($style['position'] == 'L') { 19365 $xpos = $this->lMargin; 19366 } elseif ($style['position'] == 'C') { 19367 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 19368 } elseif ($style['position'] == 'R') { 19369 $xpos = $this->w - $this->rMargin - $w; 19370 } else { 19371 $xpos = $x; 19372 } 19373 $this->img_rb_x = $xpos + $w; 19374 } 19375 $xpos_rect = $xpos; 19376 if (!isset($style['align'])) { 19377 $style['align'] = 'C'; 19378 } 19379 switch ($style['align']) { 19380 case 'L': { 19381 $xpos = $xpos_rect + $hpadding; 19382 break; 19383 } 19384 case 'R': { 19385 $xpos = $xpos_rect + ($w - ($arrcode['maxw'] * $xres)) - $hpadding; 19386 break; 19387 } 19388 case 'C': 19389 default : { 19390 $xpos = $xpos_rect + (($w - ($arrcode['maxw'] * $xres)) / 2); 19391 break; 19392 } 19393 } 19394 $xpos_text = $xpos; 19395 // barcode is always printed in LTR direction 19396 $tempRTL = $this->rtl; 19397 $this->rtl = false; 19398 // print background color 19399 if ($style['bgcolor']) { 19400 $this->Rect($xpos_rect, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']); 19401 } elseif ($style['border']) { 19402 $this->Rect($xpos_rect, $y, $w, $h, 'D'); 19403 } 19404 // set foreground color 19405 $this->SetDrawColorArray($style['fgcolor']); 19406 $this->SetTextColorArray($style['fgcolor']); 19407 // print bars 19408 foreach ($arrcode['bcode'] as $k => $v) { 19409 $bw = ($v['w'] * $xres); 19410 if ($v['t']) { 19411 // draw a vertical bar 19412 $ypos = $y + $vpadding + ($v['p'] * $barh / $arrcode['maxh']); 19413 $this->Rect($xpos, $ypos, $bw, ($v['h'] * $barh / $arrcode['maxh']), 'F', array(), $style['fgcolor']); 19414 } 19415 $xpos += $bw; 19416 } 19417 // print text 19418 if ($style['text']) { 19419 if (isset($style['label']) AND !$this->empty_string($style['label'])) { 19420 $label = $style['label']; 19421 } else { 19422 $label = $code; 19423 } 19424 $txtwidth = ($arrcode['maxw'] * $xres); 19425 if ($this->GetStringWidth($label) > $txtwidth) { 19426 $style['stretchtext'] = 2; 19427 } 19428 // print text 19429 $this->x = $xpos_text; 19430 $this->y = $y + $vpadding + $barh; 19431 $cellpadding = $this->cell_padding; 19432 $this->SetCellPadding(0); 19433 $this->Cell($txtwidth, '', $label, 0, 0, 'C', false, '', $style['stretchtext'], false, 'T', 'T'); 19434 $this->cell_padding = $cellpadding; 19435 } 19436 // restore original direction 19437 $this->rtl = $tempRTL; 19438 // restore previous settings 19439 $this->setGraphicVars($gvars); 19440 // set pointer to align the next text/objects 19441 switch($align) { 19442 case 'T':{ 19443 $this->y = $y; 19444 $this->x = $this->img_rb_x; 19445 break; 19446 } 19447 case 'M':{ 19448 $this->y = $y + round($h / 2); 19449 $this->x = $this->img_rb_x; 19450 break; 19451 } 19452 case 'B':{ 19453 $this->y = $this->img_rb_y; 19454 $this->x = $this->img_rb_x; 19455 break; 19456 } 19457 case 'N':{ 19458 $this->SetY($this->img_rb_y); 19459 break; 19460 } 19461 default:{ 19462 break; 19463 } 19464 } 19465 $this->endlinex = $this->img_rb_x; 19466 } 19467 19483 public function writeBarcode($x, $y, $w, $h, $type, $style, $font, $xres, $code) { 19484 // convert old settings for the new write1DBarcode() function. 19485 $xres = 1 / $xres; 19486 $newstyle = array( 19487 'position' => '', 19488 'align' => '', 19489 'stretch' => false, 19490 'fitwidth' => false, 19491 'cellfitalign' => '', 19492 'border' => false, 19493 'padding' => 0, 19494 'fgcolor' => array(0,0,0), 19495 'bgcolor' => false, 19496 'text' => true, 19497 'font' => $font, 19498 'fontsize' => 8, 19499 'stretchtext' => 4 19500 ); 19501 if ($style & 1) { 19502 $newstyle['border'] = true; 19503 } 19504 if ($style & 2) { 19505 $newstyle['bgcolor'] = false; 19506 } 19507 if ($style & 4) { 19508 $newstyle['position'] = 'C'; 19509 } elseif ($style & 8) { 19510 $newstyle['position'] = 'L'; 19511 } elseif ($style & 16) { 19512 $newstyle['position'] = 'R'; 19513 } 19514 if ($style & 128) { 19515 $newstyle['text'] = true; 19516 } 19517 if ($style & 256) { 19518 $newstyle['stretchtext'] = 4; 19519 } 19520 $this->write1DBarcode($code, $type, $x, $y, $w, $h, $xres, $newstyle, ''); 19521 } 19522 19548 public function write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='', $distort=false) { 19549 if ($this->empty_string(trim($code))) { 19550 return; 19551 } 19552 require_once(dirname(__FILE__).'/2dbarcodes.php'); 19553 // save current graphic settings 19554 $gvars = $this->getGraphicVars(); 19555 // create new barcode object 19556 $barcodeobj = new TCPDF2DBarcode($code, $type); 19557 $arrcode = $barcodeobj->getBarcodeArray(); 19558 if (($arrcode === false) OR empty($arrcode)) { 19559 $this->Error('Error in 2D barcode string'); 19560 } 19561 // set default values 19562 if (!isset($style['position'])) { 19563 $style['position'] = ''; 19564 } 19565 if (!isset($style['fgcolor'])) { 19566 $style['fgcolor'] = array(0,0,0); // default black 19567 } 19568 if (!isset($style['bgcolor'])) { 19569 $style['bgcolor'] = false; // default transparent 19570 } 19571 if (!isset($style['border'])) { 19572 $style['border'] = false; 19573 } 19574 // padding 19575 if (!isset($style['padding'])) { 19576 $style['padding'] = 0; 19577 } elseif ($style['padding'] === 'auto') { 19578 $style['padding'] = 4; 19579 } 19580 if (!isset($style['hpadding'])) { 19581 $style['hpadding'] = $style['padding']; 19582 } elseif ($style['hpadding'] === 'auto') { 19583 $style['hpadding'] = 4; 19584 } 19585 if (!isset($style['vpadding'])) { 19586 $style['vpadding'] = $style['padding']; 19587 } elseif ($style['vpadding'] === 'auto') { 19588 $style['vpadding'] = 4; 19589 } 19590 $hpad = (2 * $style['hpadding']); 19591 $vpad = (2 * $style['vpadding']); 19592 // cell (module) dimension 19593 if (!isset($style['module_width'])) { 19594 $style['module_width'] = 1; // width of a single module in points 19595 } 19596 if (!isset($style['module_height'])) { 19597 $style['module_height'] = 1; // height of a single module in points 19598 } 19599 if ($x === '') { 19600 $x = $this->x; 19601 } 19602 if ($y === '') { 19603 $y = $this->y; 19604 } 19605 // check page for no-write regions and adapt page margins if necessary 19606 list($x, $y) = $this->checkPageRegions($h, $x, $y); 19607 // number of barcode columns and rows 19608 $rows = $arrcode['num_rows']; 19609 $cols = $arrcode['num_cols']; 19610 // module width and height 19611 $mw = $style['module_width']; 19612 $mh = $style['module_height']; 19613 // get max dimensions 19614 if ($this->rtl) { 19615 $maxw = $x - $this->lMargin; 19616 } else { 19617 $maxw = $this->w - $this->rMargin - $x; 19618 } 19619 $maxh = ($this->h - $this->tMargin - $this->bMargin); 19620 $ratioHW = ((($rows * $mh) + $hpad) / (($cols * $mw) + $vpad)); 19621 $ratioWH = ((($cols * $mw) + $vpad) / (($rows * $mh) + $hpad)); 19622 if (!$distort) { 19623 if (($maxw * $ratioHW) > $maxh) { 19624 $maxw = $maxh * $ratioWH; 19625 } 19626 if (($maxh * $ratioWH) > $maxw) { 19627 $maxh = $maxw * $ratioHW; 19628 } 19629 } 19630 // set maximum dimesions 19631 if ($w > $maxw) { 19632 $w = $maxw; 19633 } 19634 if ($h > $maxh) { 19635 $h = $maxh; 19636 } 19637 // set dimensions 19638 if ((($w === '') OR ($w <= 0)) AND (($h === '') OR ($h <= 0))) { 19639 $w = ($cols + $hpad) * ($mw / $this->k); 19640 $h = ($rows + $vpad) * ($mh / $this->k); 19641 } elseif (($w === '') OR ($w <= 0)) { 19642 $w = $h * $ratioWH; 19643 } elseif (($h === '') OR ($h <= 0)) { 19644 $h = $w * $ratioHW; 19645 } 19646 // barcode size (excluding padding) 19647 $bw = ($w * $cols) / ($cols + $hpad); 19648 $bh = ($h * $rows) / ($rows + $vpad); 19649 // dimension of single barcode cell unit 19650 $cw = $bw / $cols; 19651 $ch = $bh / $rows; 19652 if (!$distort) { 19653 if (($cw / $ch) > ($mw / $mh)) { 19654 // correct horizontal distortion 19655 $cw = $ch * $mw / $mh; 19656 $bw = $cw * $cols; 19657 $style['hpadding'] = ($w - $bw) / (2 * $cw); 19658 } else { 19659 // correct vertical distortion 19660 $ch = $cw * $mh / $mw; 19661 $bh = $ch * $rows; 19662 $style['vpadding'] = ($h - $bh) / (2 * $ch); 19663 } 19664 } 19665 // fit the barcode on available space 19666 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false); 19667 // set alignment 19668 $this->img_rb_y = $y + $h; 19669 // set alignment 19670 if ($this->rtl) { 19671 if ($style['position'] == 'L') { 19672 $xpos = $this->lMargin; 19673 } elseif ($style['position'] == 'C') { 19674 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 19675 } elseif ($style['position'] == 'R') { 19676 $xpos = $this->w - $this->rMargin - $w; 19677 } else { 19678 $xpos = $x - $w; 19679 } 19680 $this->img_rb_x = $xpos; 19681 } else { 19682 if ($style['position'] == 'L') { 19683 $xpos = $this->lMargin; 19684 } elseif ($style['position'] == 'C') { 19685 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 19686 } elseif ($style['position'] == 'R') { 19687 $xpos = $this->w - $this->rMargin - $w; 19688 } else { 19689 $xpos = $x; 19690 } 19691 $this->img_rb_x = $xpos + $w; 19692 } 19693 $xstart = $xpos + ($style['hpadding'] * $cw); 19694 $ystart = $y + ($style['vpadding'] * $ch); 19695 // barcode is always printed in LTR direction 19696 $tempRTL = $this->rtl; 19697 $this->rtl = false; 19698 // print background color 19699 if ($style['bgcolor']) { 19700 $this->Rect($xpos, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']); 19701 } elseif ($style['border']) { 19702 $this->Rect($xpos, $y, $w, $h, 'D'); 19703 } 19704 // set foreground color 19705 $this->SetDrawColorArray($style['fgcolor']); 19706 // print barcode cells 19707 // for each row 19708 for ($r = 0; $r < $rows; ++$r) { 19709 $xr = $xstart; 19710 // for each column 19711 for ($c = 0; $c < $cols; ++$c) { 19712 if ($arrcode['bcode'][$r][$c] == 1) { 19713 // draw a single barcode cell 19714 $this->Rect($xr, $ystart, $cw, $ch, 'F', array(), $style['fgcolor']); 19715 } 19716 $xr += $cw; 19717 } 19718 $ystart += $ch; 19719 } 19720 // restore original direction 19721 $this->rtl = $tempRTL; 19722 // restore previous settings 19723 $this->setGraphicVars($gvars); 19724 // set pointer to align the next text/objects 19725 switch($align) { 19726 case 'T':{ 19727 $this->y = $y; 19728 $this->x = $this->img_rb_x; 19729 break; 19730 } 19731 case 'M':{ 19732 $this->y = $y + round($h/2); 19733 $this->x = $this->img_rb_x; 19734 break; 19735 } 19736 case 'B':{ 19737 $this->y = $this->img_rb_y; 19738 $this->x = $this->img_rb_x; 19739 break; 19740 } 19741 case 'N':{ 19742 $this->SetY($this->img_rb_y); 19743 break; 19744 } 19745 default:{ 19746 break; 19747 } 19748 } 19749 $this->endlinex = $this->img_rb_x; 19750 } 19751 19771 public function getMargins() { 19772 $ret = array( 19773 'left' => $this->lMargin, 19774 'right' => $this->rMargin, 19775 'top' => $this->tMargin, 19776 'bottom' => $this->bMargin, 19777 'header' => $this->header_margin, 19778 'footer' => $this->footer_margin, 19779 'cell' => $this->cell_padding, 19780 'padding_left' => $this->cell_padding['L'], 19781 'padding_top' => $this->cell_padding['T'], 19782 'padding_right' => $this->cell_padding['R'], 19783 'padding_bottom' => $this->cell_padding['B'] 19784 ); 19785 return $ret; 19786 } 19787 19798 public function getOriginalMargins() { 19799 $ret = array( 19800 'left' => $this->original_lMargin, 19801 'right' => $this->original_rMargin 19802 ); 19803 return $ret; 19804 } 19805 19812 public function getFontSize() { 19813 return $this->FontSize; 19814 } 19815 19822 public function getFontSizePt() { 19823 return $this->FontSizePt; 19824 } 19825 19832 public function getFontFamily() { 19833 return $this->FontFamily; 19834 } 19835 19842 public function getFontStyle() { 19843 return $this->FontStyle; 19844 } 19845 19858 public function fixHTMLCode($html, $default_css='', $tagvs='', $tidy_options='') { 19859 // configure parameters for HTML Tidy 19860 if ($tidy_options === '') { 19861 $tidy_options = array ( 19862 'clean' => 1, 19863 'drop-empty-paras' => 0, 19864 'drop-proprietary-attributes' => 1, 19865 'fix-backslash' => 1, 19866 'hide-comments' => 1, 19867 'join-styles' => 1, 19868 'lower-literals' => 1, 19869 'merge-divs' => 1, 19870 'merge-spans' => 1, 19871 'output-xhtml' => 1, 19872 'word-2000' => 1, 19873 'wrap' => 0, 19874 'output-bom' => 0, 19875 //'char-encoding' => 'utf8', 19876 //'input-encoding' => 'utf8', 19877 //'output-encoding' => 'utf8' 19878 ); 19879 } 19880 // clean up the HTML code 19881 $tidy = tidy_parse_string($html, $tidy_options); 19882 // fix the HTML 19883 $tidy->cleanRepair(); 19884 // get the CSS part 19885 $tidy_head = tidy_get_head($tidy); 19886 $css = $tidy_head->value; 19887 $css = preg_replace('/<style([^>]+)>/ims', '<style>', $css); 19888 $css = preg_replace('/<\/style>(.*)<style>/ims', "\n", $css); 19889 $css = str_replace('/*<![CDATA[*/', '', $css); 19890 $css = str_replace('/*]]>*/', '', $css); 19891 preg_match('/<style>(.*)<\/style>/ims', $css, $matches); 19892 if (isset($matches[1])) { 19893 $css = strtolower($matches[1]); 19894 } else { 19895 $css = ''; 19896 } 19897 // include default css 19898 $css = '<style>'.$default_css.$css.'</style>'; 19899 // get the body part 19900 $tidy_body = tidy_get_body($tidy); 19901 $html = $tidy_body->value; 19902 // fix some self-closing tags 19903 $html = str_replace('<br>', '<br />', $html); 19904 // remove some empty tag blocks 19905 $html = preg_replace('/<div([^>]*)><\/div>/', '', $html); 19906 $html = preg_replace('/<p([^>]*)><\/p>/', '', $html); 19907 if ($tagvs !== '') { 19908 // set vertical space for some XHTML tags 19909 $this->setHtmlVSpace($tagvs); 19910 } 19911 // return the cleaned XHTML code + CSS 19912 return $css.$html; 19913 } 19914 19923 protected function extractCSSproperties($cssdata) { 19924 if (empty($cssdata)) { 19925 return array(); 19926 } 19927 // remove comments 19928 $cssdata = preg_replace('/\/\*[^\*]*\*\//', '', $cssdata); 19929 // remove newlines and multiple spaces 19930 $cssdata = preg_replace('/[\s]+/', ' ', $cssdata); 19931 // remove some spaces 19932 $cssdata = preg_replace('/[\s]*([;:\{\}]{1})[\s]*/', '\\1', $cssdata); 19933 // remove empty blocks 19934 $cssdata = preg_replace('/([^\}\{]+)\{\}/', '', $cssdata); 19935 // replace media type parenthesis 19936 $cssdata = preg_replace('/@media[\s]+([^\{]*)\{/i', '@media \\1§', $cssdata); 19937 $cssdata = preg_replace('/\}\}/si', '}§', $cssdata); 19938 // trim string 19939 $cssdata = trim($cssdata); 19940 // find media blocks (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv) 19941 $cssblocks = array(); 19942 $matches = array(); 19943 if (preg_match_all('/@media[\s]+([^\§]*)§([^§]*)§/i', $cssdata, $matches) > 0) { 19944 foreach ($matches[1] as $key => $type) { 19945 $cssblocks[$type] = $matches[2][$key]; 19946 } 19947 // remove media blocks 19948 $cssdata = preg_replace('/@media[\s]+([^\§]*)§([^§]*)§/i', '', $cssdata); 19949 } 19950 // keep 'all' and 'print' media, other media types are discarded 19951 if (isset($cssblocks['all']) AND !empty($cssblocks['all'])) { 19952 $cssdata .= $cssblocks['all']; 19953 } 19954 if (isset($cssblocks['print']) AND !empty($cssblocks['print'])) { 19955 $cssdata .= $cssblocks['print']; 19956 } 19957 // reset css blocks array 19958 $cssblocks = array(); 19959 $matches = array(); 19960 // explode css data string into array 19961 if (substr($cssdata, -1) == '}') { 19962 // remove last parethesis 19963 $cssdata = substr($cssdata, 0, -1); 19964 } 19965 $matches = explode('}', $cssdata); 19966 foreach ($matches as $key => $block) { 19967 // index 0 contains the CSS selector, index 1 contains CSS properties 19968 $cssblocks[$key] = explode('{', $block); 19969 if (!isset($cssblocks[$key][1])) { 19970 // remove empty definitions 19971 unset($cssblocks[$key]); 19972 } 19973 } 19974 // split groups of selectors (comma-separated list of selectors) 19975 foreach ($cssblocks as $key => $block) { 19976 if (strpos($block[0], ',') > 0) { 19977 $selectors = explode(',', $block[0]); 19978 foreach ($selectors as $sel) { 19979 $cssblocks[] = array(0 => trim($sel), 1 => $block[1]); 19980 } 19981 unset($cssblocks[$key]); 19982 } 19983 } 19984 // covert array to selector => properties 19985 $cssdata = array(); 19986 foreach ($cssblocks as $block) { 19987 $selector = $block[0]; 19988 // calculate selector's specificity 19989 $matches = array(); 19990 $a = 0; // the declaration is not from is a 'style' attribute 19991 $b = intval(preg_match_all('/[\#]/', $selector, $matches)); // number of ID attributes 19992 $c = intval(preg_match_all('/[\[\.]/', $selector, $matches)); // number of other attributes 19993 $c += intval(preg_match_all('/[\:]link|visited|hover|active|focus|target|lang|enabled|disabled|checked|indeterminate|root|nth|first|last|only|empty|contains|not/i', $selector, $matches)); // number of pseudo-classes 19994 $d = intval(preg_match_all('/[>\+\~\s]{1}[a-zA-Z0-9]+/', ' '.$selector, $matches)); // number of element names 19995 $d += intval(preg_match_all('/[\:][\:]/', $selector, $matches)); // number of pseudo-elements 19996 $specificity = $a.$b.$c.$d; 19997 // add specificity to the beginning of the selector 19998 $cssdata[$specificity.' '.$selector] = $block[1]; 19999 } 20000 // sort selectors alphabetically to account for specificity 20001 ksort($cssdata, SORT_STRING); 20002 // return array 20003 return $cssdata; 20004 } 20005 20015 protected function isValidCSSSelectorForTag($dom, $key, $selector) { 20016 $valid = false; // value to be returned 20017 $tag = $dom[$key]['value']; 20018 $class = array(); 20019 if (isset($dom[$key]['attribute']['class']) AND !empty($dom[$key]['attribute']['class'])) { 20020 $class = explode(' ', strtolower($dom[$key]['attribute']['class'])); 20021 } 20022 $id = ''; 20023 if (isset($dom[$key]['attribute']['id']) AND !empty($dom[$key]['attribute']['id'])) { 20024 $id = strtolower($dom[$key]['attribute']['id']); 20025 } 20026 $selector = preg_replace('/([>\+\~\s]{1})([\.]{1})([^>\+\~\s]*)/si', '\\1*.\\3', $selector); 20027 $matches = array(); 20028 if (preg_match_all('/([>\+\~\s]{1})([a-zA-Z0-9\*]+)([^>\+\~\s]*)/si', $selector, $matches, PREG_PATTERN_ORDER | PREG_OFFSET_CAPTURE) > 0) { 20029 $parentop = array_pop($matches[1]); 20030 $operator = $parentop[0]; 20031 $offset = $parentop[1]; 20032 $lasttag = array_pop($matches[2]); 20033 $lasttag = strtolower(trim($lasttag[0])); 20034 if (($lasttag == '*') OR ($lasttag == $tag)) { 20035 // the last element on selector is our tag or 'any tag' 20036 $attrib = array_pop($matches[3]); 20037 $attrib = strtolower(trim($attrib[0])); 20038 if (!empty($attrib)) { 20039 // check if matches class, id, attribute, pseudo-class or pseudo-element 20040 switch ($attrib{0}) { 20041 case '.': { // class 20042 if (in_array(substr($attrib, 1), $class)) { 20043 $valid = true; 20044 } 20045 break; 20046 } 20047 case '#': { // ID 20048 if (substr($attrib, 1) == $id) { 20049 $valid = true; 20050 } 20051 break; 20052 } 20053 case '[': { // attribute 20054 $attrmatch = array(); 20055 if (preg_match('/\[([a-zA-Z0-9]*)[\s]*([\~\^\$\*\|\=]*)[\s]*["]?([^"\]]*)["]?\]/i', $attrib, $attrmatch) > 0) { 20056 $att = strtolower($attrmatch[1]); 20057 $val = $attrmatch[3]; 20058 if (isset($dom[$key]['attribute'][$att])) { 20059 switch ($attrmatch[2]) { 20060 case '=': { 20061 if ($dom[$key]['attribute'][$att] == $val) { 20062 $valid = true; 20063 } 20064 break; 20065 } 20066 case '~=': { 20067 if (in_array($val, explode(' ', $dom[$key]['attribute'][$att]))) { 20068 $valid = true; 20069 } 20070 break; 20071 } 20072 case '^=': { 20073 if ($val == substr($dom[$key]['attribute'][$att], 0, strlen($val))) { 20074 $valid = true; 20075 } 20076 break; 20077 } 20078 case '$=': { 20079 if ($val == substr($dom[$key]['attribute'][$att], -strlen($val))) { 20080 $valid = true; 20081 } 20082 break; 20083 } 20084 case '*=': { 20085 if (strpos($dom[$key]['attribute'][$att], $val) !== false) { 20086 $valid = true; 20087 } 20088 break; 20089 } 20090 case '|=': { 20091 if ($dom[$key]['attribute'][$att] == $val) { 20092 $valid = true; 20093 } elseif (preg_match('/'.$val.'[\-]{1}/i', $dom[$key]['attribute'][$att]) > 0) { 20094 $valid = true; 20095 } 20096 break; 20097 } 20098 default: { 20099 $valid = true; 20100 } 20101 } 20102 } 20103 } 20104 break; 20105 } 20106 case ':': { // pseudo-class or pseudo-element 20107 if ($attrib{1} == ':') { // pseudo-element 20108 // pseudo-elements are not supported! 20109 // (::first-line, ::first-letter, ::before, ::after) 20110 } else { // pseudo-class 20111 // pseudo-classes are not supported! 20112 // (:root, :nth-child(n), :nth-last-child(n), :nth-of-type(n), :nth-last-of-type(n), :first-child, :last-child, :first-of-type, :last-of-type, :only-child, :only-of-type, :empty, :link, :visited, :active, :hover, :focus, :target, :lang(fr), :enabled, :disabled, :checked) 20113 } 20114 break; 20115 } 20116 } // end of switch 20117 } else { 20118 $valid = true; 20119 } 20120 if ($valid AND ($offset > 0)) { 20121 $valid = false; 20122 // check remaining selector part 20123 $selector = substr($selector, 0, $offset); 20124 switch ($operator) { 20125 case ' ': { // descendant of an element 20126 while ($dom[$key]['parent'] > 0) { 20127 if ($this->isValidCSSSelectorForTag($dom, $dom[$key]['parent'], $selector)) { 20128 $valid = true; 20129 break; 20130 } else { 20131 $key = $dom[$key]['parent']; 20132 } 20133 } 20134 break; 20135 } 20136 case '>': { // child of an element 20137 $valid = $this->isValidCSSSelectorForTag($dom, $dom[$key]['parent'], $selector); 20138 break; 20139 } 20140 case '+': { // immediately preceded by an element 20141 for ($i = ($key - 1); $i > $dom[$key]['parent']; --$i) { 20142 if ($dom[$i]['tag'] AND $dom[$i]['opening']) { 20143 $valid = $this->isValidCSSSelectorForTag($dom, $i, $selector); 20144 break; 20145 } 20146 } 20147 break; 20148 } 20149 case '~': { // preceded by an element 20150 for ($i = ($key - 1); $i > $dom[$key]['parent']; --$i) { 20151 if ($dom[$i]['tag'] AND $dom[$i]['opening']) { 20152 if ($this->isValidCSSSelectorForTag($dom, $i, $selector)) { 20153 break; 20154 } 20155 } 20156 } 20157 break; 20158 } 20159 } 20160 } 20161 } 20162 } 20163 return $valid; 20164 } 20165 20175 protected function getCSSdataArray($dom, $key, $css) { 20176 $cssarray = array(); // style to be returned 20177 // get parent CSS selectors 20178 $selectors = array(); 20179 if (isset($dom[($dom[$key]['parent'])]['csssel'])) { 20180 $selectors = $dom[($dom[$key]['parent'])]['csssel']; 20181 } 20182 // get all styles that apply 20183 foreach($css as $selector => $style) { 20184 $pos = strpos($selector, ' '); 20185 // get specificity 20186 $specificity = substr($selector, 0, $pos); 20187 // remove specificity 20188 $selector = substr($selector, $pos); 20189 // check if this selector apply to current tag 20190 if ($this->isValidCSSSelectorForTag($dom, $key, $selector)) { 20191 if (!in_array($selector, $selectors)) { 20192 // add style if not already added on parent selector 20193 $cssarray[] = array('k' => $selector, 's' => $specificity, 'c' => $style); 20194 $selectors[] = $selector; 20195 } 20196 } 20197 } 20198 if (isset($dom[$key]['attribute']['style'])) { 20199 // attach inline style (latest properties have high priority) 20200 $cssarray[] = array('k' => '', 's' => '1000', 'c' => $dom[$key]['attribute']['style']); 20201 } 20202 // order the css array to account for specificity 20203 $cssordered = array(); 20204 foreach ($cssarray as $key => $val) { 20205 $skey = sprintf('%04d', $key); 20206 $cssordered[$val['s'].'_'.$skey] = $val; 20207 } 20208 // sort selectors alphabetically to account for specificity 20209 ksort($cssordered, SORT_STRING); 20210 return array($selectors, $cssordered); 20211 } 20212 20220 protected function getTagStyleFromCSSarray($css) { 20221 $tagstyle = ''; // value to be returned 20222 foreach ($css as $style) { 20223 // split single css commands 20224 $csscmds = explode(';', $style['c']); 20225 foreach ($csscmds as $cmd) { 20226 if (!empty($cmd)) { 20227 $pos = strpos($cmd, ':'); 20228 if ($pos !== false) { 20229 $cmd = substr($cmd, 0, ($pos + 1)); 20230 if (strpos($tagstyle, $cmd) !== false) { 20231 // remove duplicate commands (last commands have high priority) 20232 $tagstyle = preg_replace('/'.$cmd.'[^;]+/i', '', $tagstyle); 20233 } 20234 } 20235 } 20236 } 20237 $tagstyle .= ';'.$style['c']; 20238 } 20239 // remove multiple semicolons 20240 $tagstyle = preg_replace('/[;]+/', ';', $tagstyle); 20241 return $tagstyle; 20242 } 20243 20251 protected function getCSSBorderWidth($width) { 20252 if ($width == 'thin') { 20253 $width = (2 / $this->k); 20254 } elseif ($width == 'medium') { 20255 $width = (4 / $this->k); 20256 } elseif ($width == 'thick') { 20257 $width = (6 / $this->k); 20258 } else { 20259 $width = $this->getHTMLUnitToUnits($width, 1, 'px', false); 20260 } 20261 return $width; 20262 } 20263 20271 protected function getCSSBorderDashStyle($style) { 20272 switch (strtolower($style)) { 20273 case 'none': 20274 case 'hidden': { 20275 $dash = -1; 20276 break; 20277 } 20278 case 'dotted': { 20279 $dash = 1; 20280 break; 20281 } 20282 case 'dashed': { 20283 $dash = 3; 20284 break; 20285 } 20286 case 'double': 20287 case 'groove': 20288 case 'ridge': 20289 case 'inset': 20290 case 'outset': 20291 case 'solid': 20292 default: { 20293 $dash = 0; 20294 break; 20295 } 20296 } 20297 return $dash; 20298 } 20299 20307 protected function getCSSBorderStyle($cssborder) { 20308 $bprop = preg_split('/[\s]+/', trim($cssborder)); 20309 $border = array(); // value to be returned 20310 switch (count($bprop)) { 20311 case 3: { 20312 $width = $bprop[0]; 20313 $style = $bprop[1]; 20314 $color = $bprop[2]; 20315 break; 20316 } 20317 case 2: { 20318 $width = 'medium'; 20319 $style = $bprop[0]; 20320 $color = $bprop[1]; 20321 break; 20322 } 20323 case 1: { 20324 $width = 'medium'; 20325 $style = $bprop[0]; 20326 $color = 'black'; 20327 break; 20328 } 20329 default: { 20330 $width = 'medium'; 20331 $style = 'solid'; 20332 $color = 'black'; 20333 break; 20334 } 20335 } 20336 if ($style == 'none') { 20337 return array(); 20338 } 20339 $border['cap'] = 'square'; 20340 $border['join'] = 'miter'; 20341 $border['dash'] = $this->getCSSBorderDashStyle($style); 20342 if ($border['dash'] < 0) { 20343 return array(); 20344 } 20345 $border['width'] = $this->getCSSBorderWidth($width); 20346 $border['color'] = $this->convertHTMLColorToDec($color); 20347 return $border; 20348 } 20349 20358 public function getCSSPadding($csspadding, $width=0) { 20359 $padding = preg_split('/[\s]+/', trim($csspadding)); 20360 $cell_padding = array(); // value to be returned 20361 switch (count($padding)) { 20362 case 4: { 20363 $cell_padding['T'] = $padding[0]; 20364 $cell_padding['R'] = $padding[1]; 20365 $cell_padding['B'] = $padding[2]; 20366 $cell_padding['L'] = $padding[3]; 20367 break; 20368 } 20369 case 3: { 20370 $cell_padding['T'] = $padding[0]; 20371 $cell_padding['R'] = $padding[1]; 20372 $cell_padding['B'] = $padding[2]; 20373 $cell_padding['L'] = $padding[1]; 20374 break; 20375 } 20376 case 2: { 20377 $cell_padding['T'] = $padding[0]; 20378 $cell_padding['R'] = $padding[1]; 20379 $cell_padding['B'] = $padding[0]; 20380 $cell_padding['L'] = $padding[1]; 20381 break; 20382 } 20383 case 1: { 20384 $cell_padding['T'] = $padding[0]; 20385 $cell_padding['R'] = $padding[0]; 20386 $cell_padding['B'] = $padding[0]; 20387 $cell_padding['L'] = $padding[0]; 20388 break; 20389 } 20390 default: { 20391 return $this->cell_padding; 20392 } 20393 } 20394 if ($width == 0) { 20395 $width = $this->w - $this->lMargin - $this->rMargin; 20396 } 20397 $cell_padding['T'] = $this->getHTMLUnitToUnits($cell_padding['T'], $width, 'px', false); 20398 $cell_padding['R'] = $this->getHTMLUnitToUnits($cell_padding['R'], $width, 'px', false); 20399 $cell_padding['B'] = $this->getHTMLUnitToUnits($cell_padding['B'], $width, 'px', false); 20400 $cell_padding['L'] = $this->getHTMLUnitToUnits($cell_padding['L'], $width, 'px', false); 20401 return $cell_padding; 20402 } 20403 20412 public function getCSSMargin($cssmargin, $width=0) { 20413 $margin = preg_split('/[\s]+/', trim($cssmargin)); 20414 $cell_margin = array(); // value to be returned 20415 switch (count($margin)) { 20416 case 4: { 20417 $cell_margin['T'] = $margin[0]; 20418 $cell_margin['R'] = $margin[1]; 20419 $cell_margin['B'] = $margin[2]; 20420 $cell_margin['L'] = $margin[3]; 20421 break; 20422 } 20423 case 3: { 20424 $cell_margin['T'] = $margin[0]; 20425 $cell_margin['R'] = $margin[1]; 20426 $cell_margin['B'] = $margin[2]; 20427 $cell_margin['L'] = $margin[1]; 20428 break; 20429 } 20430 case 2: { 20431 $cell_margin['T'] = $margin[0]; 20432 $cell_margin['R'] = $margin[1]; 20433 $cell_margin['B'] = $margin[0]; 20434 $cell_margin['L'] = $margin[1]; 20435 break; 20436 } 20437 case 1: { 20438 $cell_margin['T'] = $margin[0]; 20439 $cell_margin['R'] = $margin[0]; 20440 $cell_margin['B'] = $margin[0]; 20441 $cell_margin['L'] = $margin[0]; 20442 break; 20443 } 20444 default: { 20445 return $this->cell_margin; 20446 } 20447 } 20448 if ($width == 0) { 20449 $width = $this->w - $this->lMargin - $this->rMargin; 20450 } 20451 $cell_margin['T'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['T']), $width, 'px', false); 20452 $cell_margin['R'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['R']), $width, 'px', false); 20453 $cell_margin['B'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['B']), $width, 'px', false); 20454 $cell_margin['L'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['L']), $width, 'px', false); 20455 return $cell_margin; 20456 } 20457 20466 public function getCSSBorderMargin($cssbspace, $width=0) { 20467 $space = preg_split('/[\s]+/', trim($cssbspace)); 20468 $border_spacing = array(); // value to be returned 20469 switch (count($space)) { 20470 case 2: { 20471 $border_spacing['H'] = $space[0]; 20472 $border_spacing['V'] = $space[1]; 20473 break; 20474 } 20475 case 1: { 20476 $border_spacing['H'] = $space[0]; 20477 $border_spacing['V'] = $space[0]; 20478 break; 20479 } 20480 default: { 20481 return array('H' => 0, 'V' => 0); 20482 } 20483 } 20484 if ($width == 0) { 20485 $width = $this->w - $this->lMargin - $this->rMargin; 20486 } 20487 $border_spacing['H'] = $this->getHTMLUnitToUnits($border_spacing['H'], $width, 'px', false); 20488 $border_spacing['V'] = $this->getHTMLUnitToUnits($border_spacing['V'], $width, 'px', false); 20489 return $border_spacing; 20490 } 20491 20500 protected function getCSSFontSpacing($spacing, $parent=0) { 20501 $val = 0; // value to be returned 20502 $spacing = trim($spacing); 20503 switch ($spacing) { 20504 case 'normal': { 20505 $val = 0; 20506 break; 20507 } 20508 case 'inherit': { 20509 if ($parent == 'normal') { 20510 $val = 0; 20511 } else { 20512 $val = $parent; 20513 } 20514 break; 20515 } 20516 default: { 20517 $val = $this->getHTMLUnitToUnits($spacing, 0, 'px', false); 20518 } 20519 } 20520 return $val; 20521 } 20522 20531 protected function getCSSFontStretching($stretch, $parent=100) { 20532 $val = 100; // value to be returned 20533 $stretch = trim($stretch); 20534 switch ($stretch) { 20535 case 'ultra-condensed': { 20536 $val = 40; 20537 break; 20538 } 20539 case 'extra-condensed': { 20540 $val = 55; 20541 break; 20542 } 20543 case 'condensed': { 20544 $val = 70; 20545 break; 20546 } 20547 case 'semi-condensed': { 20548 $val = 85; 20549 break; 20550 } 20551 case 'normal': { 20552 $val = 100; 20553 break; 20554 } 20555 case 'semi-expanded': { 20556 $val = 115; 20557 break; 20558 } 20559 case 'expanded': { 20560 $val = 130; 20561 break; 20562 } 20563 case 'extra-expanded': { 20564 $val = 145; 20565 break; 20566 } 20567 case 'ultra-expanded': { 20568 $val = 160; 20569 break; 20570 } 20571 case 'wider': { 20572 $val = $parent + 10; 20573 break; 20574 } 20575 case 'narrower': { 20576 $val = $parent - 10; 20577 break; 20578 } 20579 case 'inherit': { 20580 if ($parent == 'normal') { 20581 $val = 100; 20582 } else { 20583 $val = $parent; 20584 } 20585 break; 20586 } 20587 default: { 20588 $val = $this->getHTMLUnitToUnits($stretch, 100, '%', false); 20589 } 20590 } 20591 return $val; 20592 } 20593 20601 protected function getHtmlDomArray($html) { 20602 // array of CSS styles ( selector => properties). 20603 $css = array(); 20604 // get CSS array defined at previous call 20605 $matches = array(); 20606 if (preg_match_all('/<cssarray>([^<]*)<\/cssarray>/isU', $html, $matches) > 0) { 20607 if (isset($matches[1][0])) { 20608 $css = array_merge($css, unserialize($this->unhtmlentities($matches[1][0]))); 20609 } 20610 $html = preg_replace('/<cssarray>(.*?)<\/cssarray>/isU', '', $html); 20611 } 20612 // extract external CSS files 20613 $matches = array(); 20614 if (preg_match_all('/<link([^>]*)>/isU', $html, $matches) > 0) { 20615 foreach ($matches[1] as $key => $link) { 20616 $type = array(); 20617 if (preg_match('/type[\s]*=[\s]*"text\/css"/', $link, $type)) { 20618 $type = array(); 20619 preg_match('/media[\s]*=[\s]*"([^"]*)"/', $link, $type); 20620 // get 'all' and 'print' media, other media types are discarded 20621 // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv) 20622 if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) { 20623 $type = array(); 20624 if (preg_match('/href[\s]*=[\s]*"([^"]*)"/', $link, $type) > 0) { 20625 // read CSS data file 20626 $cssdata = file_get_contents(trim($type[1])); 20627 $css = array_merge($css, $this->extractCSSproperties($cssdata)); 20628 } 20629 } 20630 } 20631 } 20632 } 20633 // extract style tags 20634 $matches = array(); 20635 if (preg_match_all('/<style([^>]*)>([^<]*)<\/style>/isU', $html, $matches) > 0) { 20636 foreach ($matches[1] as $key => $media) { 20637 $type = array(); 20638 preg_match('/media[\s]*=[\s]*"([^"]*)"/', $media, $type); 20639 // get 'all' and 'print' media, other media types are discarded 20640 // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv) 20641 if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) { 20642 $cssdata = $matches[2][$key]; 20643 $css = array_merge($css, $this->extractCSSproperties($cssdata)); 20644 } 20645 } 20646 } 20647 // create a special tag to contain the CSS array (used for table content) 20648 $csstagarray = '<cssarray>'.htmlentities(serialize($css)).'</cssarray>'; 20649 // remove head and style blocks 20650 $html = preg_replace('/<head([^>]*)>(.*?)<\/head>/siU', '', $html); 20651 $html = preg_replace('/<style([^>]*)>([^<]*)<\/style>/isU', '', $html); 20652 // define block tags 20653 $blocktags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table','tr','td'); 20654 // define self-closing tags 20655 $selfclosingtags = array('area','base','basefont','br','hr','input','img','link','meta'); 20656 // remove all unsupported tags (the line below lists all supported tags) 20657 $html = strip_tags($html, '<marker/><a><b><blockquote><body><br><br/><dd><del><div><dl><dt><em><font><form><h1><h2><h3><h4><h5><h6><hr><hr/><i><img><input><label><li><ol><option><p><pre><s><select><small><span><strike><strong><sub><sup><table><tablehead><tcpdf><td><textarea><th><thead><tr><tt><u><ul>'); 20658 //replace some blank characters 20659 $html = preg_replace('/<pre/', '<xre', $html); // preserve pre tag 20660 $html = preg_replace('/<(table|tr|td|th|tcpdf|blockquote|dd|div|dl|dt|form|h1|h2|h3|h4|h5|h6|br|hr|li|ol|ul|p)([^>]*)>[\n\r\t]+/', '<\\1\\2>', $html); 20661 $html = preg_replace('@(\r\n|\r)@', "\n", $html); 20662 $repTable = array("\t" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\"); 20663 $html = strtr($html, $repTable); 20664 $offset = 0; 20665 while (($offset < strlen($html)) AND ($pos = strpos($html, '</pre>', $offset)) !== false) { 20666 $html_a = substr($html, 0, $offset); 20667 $html_b = substr($html, $offset, ($pos - $offset + 6)); 20668 while (preg_match("'<xre([^>]*)>(.*?)\n(.*?)</pre>'si", $html_b)) { 20669 // preserve newlines on <pre> tag 20670 $html_b = preg_replace("'<xre([^>]*)>(.*?)\n(.*?)</pre>'si", "<xre\\1>\\2<br />\\3</pre>", $html_b); 20671 } 20672 while (preg_match("'<xre([^>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], $html_b)) { 20673 // preserve spaces on <pre> tag 20674 $html_b = preg_replace("'<xre([^>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], "<xre\\1>\\2 \\3</pre>", $html_b); 20675 } 20676 $html = $html_a.$html_b.substr($html, $pos + 6); 20677 $offset = strlen($html_a.$html_b); 20678 } 20679 $offset = 0; 20680 while (($offset < strlen($html)) AND ($pos = strpos($html, '</textarea>', $offset)) !== false) { 20681 $html_a = substr($html, 0, $offset); 20682 $html_b = substr($html, $offset, ($pos - $offset + 11)); 20683 while (preg_match("'<textarea([^>]*)>(.*?)\n(.*?)</textarea>'si", $html_b)) { 20684 // preserve newlines on <textarea> tag 20685 $html_b = preg_replace("'<textarea([^>]*)>(.*?)\n(.*?)</textarea>'si", "<textarea\\1>\\2<TBR>\\3</textarea>", $html_b); 20686 $html_b = preg_replace("'<textarea([^>]*)>(.*?)[\"](.*?)</textarea>'si", "<textarea\\1>\\2''\\3</textarea>", $html_b); 20687 } 20688 $html = $html_a.$html_b.substr($html, $pos + 11); 20689 $offset = strlen($html_a.$html_b); 20690 } 20691 $html = preg_replace('/([\s]*)<option/si', '<option', $html); 20692 $html = preg_replace('/<\/option>([\s]*)/si', '</option>', $html); 20693 $offset = 0; 20694 while (($offset < strlen($html)) AND ($pos = strpos($html, '</option>', $offset)) !== false) { 20695 $html_a = substr($html, 0, $offset); 20696 $html_b = substr($html, $offset, ($pos - $offset + 9)); 20697 while (preg_match("'<option([^>]*)>(.*?)</option>'si", $html_b)) { 20698 $html_b = preg_replace("'<option([\s]+)value=\"([^\"]*)\"([^>]*)>(.*?)</option>'si", "\\2#!TaB!#\\4#!NwL!#", $html_b); 20699 $html_b = preg_replace("'<option([^>]*)>(.*?)</option>'si", "\\2#!NwL!#", $html_b); 20700 } 20701 $html = $html_a.$html_b.substr($html, $pos + 9); 20702 $offset = strlen($html_a.$html_b); 20703 } 20704 if (preg_match("'</select'si", $html)) { 20705 $html = preg_replace("'<select([^>]*)>'si", "<select\\1 opt=\"", $html); 20706 $html = preg_replace("'#!NwL!#</select>'si", "\" />", $html); 20707 } 20708 $html = str_replace("\n", ' ', $html); 20709 // restore textarea newlines 20710 $html = str_replace('<TBR>', "\n", $html); 20711 // remove extra spaces from code 20712 $html = preg_replace('/[\s]+<\/(table|tr|ul|ol|dl)>/', '</\\1>', $html); 20713 $html = preg_replace('/'.$this->re_space['p'].'+<\/(td|th|li|dt|dd)>/'.$this->re_space['m'], '</\\1>', $html); 20714 $html = preg_replace('/[\s]+<(tr|td|th|li|dt|dd)/', '<\\1', $html); 20715 $html = preg_replace('/'.$this->re_space['p'].'+<(ul|ol|dl|br)/'.$this->re_space['m'], '<\\1', $html); 20716 $html = preg_replace('/<\/(table|tr|td|th|blockquote|dd|dt|dl|div|dt|h1|h2|h3|h4|h5|h6|hr|li|ol|ul|p)>[\s]+</', '</\\1><', $html); 20717 $html = preg_replace('/<\/(td|th)>/', '<marker style="font-size:0"/></\\1>', $html); 20718 $html = preg_replace('/<\/table>([\s]*)<marker style="font-size:0"\/>/', '</table>', $html); 20719 $html = preg_replace('/'.$this->re_space['p'].'+<img/'.$this->re_space['m'], chr(32).'<img', $html); 20720 $html = preg_replace('/<img([^>]*)>[\s]+([^<])/xi', '<img\\1> \\2', $html); 20721 $html = preg_replace('/<img([^>]*)>/xi', '<img\\1><span><marker style="font-size:0"/></span>', $html); 20722 $html = preg_replace('/<xre/', '<pre', $html); // restore pre tag 20723 $html = preg_replace('/<textarea([^>]*)>([^<]*)<\/textarea>/xi', '<textarea\\1 value="\\2" />', $html); 20724 $html = preg_replace('/<li([^>]*)><\/li>/', '<li\\1> </li>', $html); 20725 $html = preg_replace('/<li([^>]*)>'.$this->re_space['p'].'*<img/'.$this->re_space['m'], '<li\\1><font size="1"> </font><img', $html); 20726 $html = preg_replace('/<([^>\/]*)>[\s]/', '<\\1> ', $html); // preserve some spaces 20727 $html = preg_replace('/[\s]<\/([^>]*)>/', ' </\\1>', $html); // preserve some spaces 20728 $html = preg_replace('/'.$this->re_space['p'].'+/'.$this->re_space['m'], chr(32), $html); // replace multiple spaces with a single space 20729 // trim string 20730 $html = $this->stringTrim($html); 20731 // fix first image tag alignment 20732 $html = preg_replace('/^<img/', '<span style="font-size:0"><br /></span> <img', $html, 1); 20733 // pattern for generic tag 20734 $tagpattern = '/(<[^>]+>)/'; 20735 // explodes the string 20736 $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); 20737 // count elements 20738 $maxel = count($a); 20739 $elkey = 0; 20740 $key = 0; 20741 // create an array of elements 20742 $dom = array(); 20743 $dom[$key] = array(); 20744 // set inheritable properties fot the first void element 20745 // possible inheritable properties are: azimuth, border-collapse, border-spacing, caption-side, color, cursor, direction, empty-cells, font, font-family, font-stretch, font-size, font-size-adjust, font-style, font-variant, font-weight, letter-spacing, line-height, list-style, list-style-image, list-style-position, list-style-type, orphans, page, page-break-inside, quotes, speak, speak-header, text-align, text-indent, text-transform, volume, white-space, widows, word-spacing 20746 $dom[$key]['tag'] = false; 20747 $dom[$key]['block'] = false; 20748 $dom[$key]['value'] = ''; 20749 $dom[$key]['parent'] = 0; 20750 $dom[$key]['hide'] = false; 20751 $dom[$key]['fontname'] = $this->FontFamily; 20752 $dom[$key]['fontstyle'] = $this->FontStyle; 20753 $dom[$key]['fontsize'] = $this->FontSizePt; 20754 $dom[$key]['font-stretch'] = $this->font_stretching; 20755 $dom[$key]['letter-spacing'] = $this->font_spacing; 20756 $dom[$key]['stroke'] = $this->textstrokewidth; 20757 $dom[$key]['fill'] = (($this->textrendermode % 2) == 0); 20758 $dom[$key]['clip'] = ($this->textrendermode > 3); 20759 $dom[$key]['line-height'] = $this->cell_height_ratio; 20760 $dom[$key]['bgcolor'] = false; 20761 $dom[$key]['fgcolor'] = $this->fgcolor; // color 20762 $dom[$key]['strokecolor'] = $this->strokecolor; 20763 $dom[$key]['align'] = ''; 20764 $dom[$key]['listtype'] = ''; 20765 $dom[$key]['text-indent'] = 0; 20766 $dom[$key]['border'] = array(); 20767 $dom[$key]['dir'] = $this->rtl?'rtl':'ltr'; 20768 $thead = false; // true when we are inside the THEAD tag 20769 ++$key; 20770 $level = array(); 20771 array_push($level, 0); // root 20772 while ($elkey < $maxel) { 20773 $dom[$key] = array(); 20774 $element = $a[$elkey]; 20775 $dom[$key]['elkey'] = $elkey; 20776 if (preg_match($tagpattern, $element)) { 20777 // html tag 20778 $element = substr($element, 1, -1); 20779 // get tag name 20780 preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag); 20781 $tagname = strtolower($tag[1]); 20782 // check if we are inside a table header 20783 if ($tagname == 'thead') { 20784 if ($element{0} == '/') { 20785 $thead = false; 20786 } else { 20787 $thead = true; 20788 } 20789 ++$elkey; 20790 continue; 20791 } 20792 $dom[$key]['tag'] = true; 20793 $dom[$key]['value'] = $tagname; 20794 if (in_array($dom[$key]['value'], $blocktags)) { 20795 $dom[$key]['block'] = true; 20796 } else { 20797 $dom[$key]['block'] = false; 20798 } 20799 if ($element{0} == '/') { 20800 // *** closing html tag 20801 $dom[$key]['opening'] = false; 20802 $dom[$key]['parent'] = end($level); 20803 array_pop($level); 20804 $dom[$key]['hide'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['hide']; 20805 $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname']; 20806 $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle']; 20807 $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize']; 20808 $dom[$key]['font-stretch'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['font-stretch']; 20809 $dom[$key]['letter-spacing'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['letter-spacing']; 20810 $dom[$key]['stroke'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['stroke']; 20811 $dom[$key]['fill'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fill']; 20812 $dom[$key]['clip'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['clip']; 20813 $dom[$key]['line-height'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['line-height']; 20814 $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor']; 20815 $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor']; 20816 $dom[$key]['strokecolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['strokecolor']; 20817 $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align']; 20818 $dom[$key]['dir'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['dir']; 20819 if (isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'])) { 20820 $dom[$key]['listtype'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype']; 20821 } 20822 // set the number of columns in table tag 20823 if (($dom[$key]['value'] == 'tr') AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) { 20824 $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols']; 20825 } 20826 if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) { 20827 $dom[($dom[$key]['parent'])]['content'] = $csstagarray; 20828 for ($i = ($dom[$key]['parent'] + 1); $i < $key; ++$i) { 20829 $dom[($dom[$key]['parent'])]['content'] .= $a[$dom[$i]['elkey']]; 20830 } 20831 $key = $i; 20832 // mark nested tables 20833 $dom[($dom[$key]['parent'])]['content'] = str_replace('<table', '<table nested="true"', $dom[($dom[$key]['parent'])]['content']); 20834 // remove thead sections from nested tables 20835 $dom[($dom[$key]['parent'])]['content'] = str_replace('<thead>', '', $dom[($dom[$key]['parent'])]['content']); 20836 $dom[($dom[$key]['parent'])]['content'] = str_replace('</thead>', '', $dom[($dom[$key]['parent'])]['content']); 20837 } 20838 // store header rows on a new table 20839 if (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['thead'] === true)) { 20840 if ($this->empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) { 20841 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $csstagarray.$a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']]; 20842 } 20843 for ($i = $dom[$key]['parent']; $i <= $key; ++$i) { 20844 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']]; 20845 } 20846 if (!isset($dom[($dom[$key]['parent'])]['attribute'])) { 20847 $dom[($dom[$key]['parent'])]['attribute'] = array(); 20848 } 20849 // header elements must be always contained in a single page 20850 $dom[($dom[$key]['parent'])]['attribute']['nobr'] = 'true'; 20851 } 20852 if (($dom[$key]['value'] == 'table') AND (!$this->empty_string($dom[($dom[$key]['parent'])]['thead']))) { 20853 // remove the nobr attributes from the table header 20854 $dom[($dom[$key]['parent'])]['thead'] = str_replace(' nobr="true"', '', $dom[($dom[$key]['parent'])]['thead']); 20855 $dom[($dom[$key]['parent'])]['thead'] .= '</tablehead>'; 20856 } 20857 } else { 20858 // *** opening or self-closing html tag 20859 $dom[$key]['opening'] = true; 20860 $dom[$key]['parent'] = end($level); 20861 if ((substr($element, -1, 1) == '/') OR (in_array($dom[$key]['value'], $selfclosingtags))) { 20862 // self-closing tag 20863 $dom[$key]['self'] = true; 20864 } else { 20865 // opening tag 20866 array_push($level, $key); 20867 $dom[$key]['self'] = false; 20868 } 20869 // copy some values from parent 20870 $parentkey = 0; 20871 if ($key > 0) { 20872 $parentkey = $dom[$key]['parent']; 20873 $dom[$key]['hide'] = $dom[$parentkey]['hide']; 20874 $dom[$key]['fontname'] = $dom[$parentkey]['fontname']; 20875 $dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle']; 20876 $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize']; 20877 $dom[$key]['font-stretch'] = $dom[$parentkey]['font-stretch']; 20878 $dom[$key]['letter-spacing'] = $dom[$parentkey]['letter-spacing']; 20879 $dom[$key]['stroke'] = $dom[$parentkey]['stroke']; 20880 $dom[$key]['fill'] = $dom[$parentkey]['fill']; 20881 $dom[$key]['clip'] = $dom[$parentkey]['clip']; 20882 $dom[$key]['line-height'] = $dom[$parentkey]['line-height']; 20883 $dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor']; 20884 $dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor']; 20885 $dom[$key]['strokecolor'] = $dom[$parentkey]['strokecolor']; 20886 $dom[$key]['align'] = $dom[$parentkey]['align']; 20887 $dom[$key]['listtype'] = $dom[$parentkey]['listtype']; 20888 $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent']; 20889 $dom[$key]['border'] = array(); 20890 $dom[$key]['dir'] = $dom[$parentkey]['dir']; 20891 } 20892 // get attributes 20893 preg_match_all('/([^=\s]*)[\s]*=[\s]*"([^"]*)"/', $element, $attr_array, PREG_PATTERN_ORDER); 20894 $dom[$key]['attribute'] = array(); // reset attribute array 20895 while (list($id, $name) = each($attr_array[1])) { 20896 $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id]; 20897 } 20898 if (!empty($css)) { 20899 // merge CSS style to current style 20900 list($dom[$key]['csssel'], $dom[$key]['cssdata']) = $this->getCSSdataArray($dom, $key, $css); 20901 $dom[$key]['attribute']['style'] = $this->getTagStyleFromCSSarray($dom[$key]['cssdata']); 20902 } 20903 // split style attributes 20904 if (isset($dom[$key]['attribute']['style']) AND !empty($dom[$key]['attribute']['style'])) { 20905 // get style attributes 20906 preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER); 20907 $dom[$key]['style'] = array(); // reset style attribute array 20908 while (list($id, $name) = each($style_array[1])) { 20909 // in case of duplicate attribute the last replace the previous 20910 $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]); 20911 } 20912 // --- get some style attributes --- 20913 // text direction 20914 if (isset($dom[$key]['style']['direction'])) { 20915 $dom[$key]['dir'] = $dom[$key]['style']['direction']; 20916 } 20917 // display 20918 if (isset($dom[$key]['style']['display'])) { 20919 $dom[$key]['hide'] = (trim(strtolower($dom[$key]['style']['display'])) == 'none'); 20920 } 20921 // font family 20922 if (isset($dom[$key]['style']['font-family'])) { 20923 $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['style']['font-family']); 20924 } 20925 // list-style-type 20926 if (isset($dom[$key]['style']['list-style-type'])) { 20927 $dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type'])); 20928 if ($dom[$key]['listtype'] == 'inherit') { 20929 $dom[$key]['listtype'] = $dom[$parentkey]['listtype']; 20930 } 20931 } 20932 // text-indent 20933 if (isset($dom[$key]['style']['text-indent'])) { 20934 $dom[$key]['text-indent'] = $this->getHTMLUnitToUnits($dom[$key]['style']['text-indent']); 20935 if ($dom[$key]['text-indent'] == 'inherit') { 20936 $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent']; 20937 } 20938 } 20939 // font size 20940 if (isset($dom[$key]['style']['font-size'])) { 20941 $fsize = trim($dom[$key]['style']['font-size']); 20942 switch ($fsize) { 20943 // absolute-size 20944 case 'xx-small': { 20945 $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 4; 20946 break; 20947 } 20948 case 'x-small': { 20949 $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 3; 20950 break; 20951 } 20952 case 'small': { 20953 $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 2; 20954 break; 20955 } 20956 case 'medium': { 20957 $dom[$key]['fontsize'] = $dom[0]['fontsize']; 20958 break; 20959 } 20960 case 'large': { 20961 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 2; 20962 break; 20963 } 20964 case 'x-large': { 20965 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 4; 20966 break; 20967 } 20968 case 'xx-large': { 20969 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 6; 20970 break; 20971 } 20972 // relative-size 20973 case 'smaller': { 20974 $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] - 3; 20975 break; 20976 } 20977 case 'larger': { 20978 $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] + 3; 20979 break; 20980 } 20981 default: { 20982 $dom[$key]['fontsize'] = $this->getHTMLUnitToUnits($fsize, $dom[$parentkey]['fontsize'], 'pt', true); 20983 } 20984 } 20985 } 20986 // font-stretch 20987 if (isset($dom[$key]['style']['font-stretch'])) { 20988 $dom[$key]['font-stretch'] = $this->getCSSFontStretching($dom[$key]['style']['font-stretch'], $dom[$parentkey]['font-stretch']); 20989 } 20990 // letter-spacing 20991 if (isset($dom[$key]['style']['letter-spacing'])) { 20992 $dom[$key]['letter-spacing'] = $this->getCSSFontSpacing($dom[$key]['style']['letter-spacing'], $dom[$parentkey]['letter-spacing']); 20993 } 20994 // line-height 20995 if (isset($dom[$key]['style']['line-height'])) { 20996 $lineheight = trim($dom[$key]['style']['line-height']); 20997 switch ($lineheight) { 20998 // A normal line height. This is default 20999 case 'normal': { 21000 $dom[$key]['line-height'] = $dom[0]['line-height']; 21001 break; 21002 } 21003 default: { 21004 if (is_numeric($lineheight)) { 21005 $lineheight = $lineheight * 100; 21006 } 21007 $dom[$key]['line-height'] = $this->getHTMLUnitToUnits($lineheight, 1, '%', true); 21008 } 21009 } 21010 } 21011 // font style 21012 if (isset($dom[$key]['style']['font-weight'])) { 21013 if (strtolower($dom[$key]['style']['font-weight']{0}) == 'n') { 21014 if (strpos($dom[$key]['fontstyle'], 'B') !== false) { 21015 $dom[$key]['fontstyle'] = str_replace('B', '', $dom[$key]['fontstyle']); 21016 } 21017 } elseif (strtolower($dom[$key]['style']['font-weight']{0}) == 'b') { 21018 $dom[$key]['fontstyle'] .= 'B'; 21019 } 21020 } 21021 if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style']{0}) == 'i')) { 21022 $dom[$key]['fontstyle'] .= 'I'; 21023 } 21024 // font color 21025 if (isset($dom[$key]['style']['color']) AND (!$this->empty_string($dom[$key]['style']['color']))) { 21026 $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['color']); 21027 } elseif ($dom[$key]['value'] == 'a') { 21028 $dom[$key]['fgcolor'] = $this->htmlLinkColorArray; 21029 } 21030 // background color 21031 if (isset($dom[$key]['style']['background-color']) AND (!$this->empty_string($dom[$key]['style']['background-color']))) { 21032 $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['background-color']); 21033 } 21034 // text-decoration 21035 if (isset($dom[$key]['style']['text-decoration'])) { 21036 $decors = explode(' ', strtolower($dom[$key]['style']['text-decoration'])); 21037 foreach ($decors as $dec) { 21038 $dec = trim($dec); 21039 if (!$this->empty_string($dec)) { 21040 if ($dec{0} == 'u') { 21041 // underline 21042 $dom[$key]['fontstyle'] .= 'U'; 21043 } elseif ($dec{0} == 'l') { 21044 // line-trough 21045 $dom[$key]['fontstyle'] .= 'D'; 21046 } elseif ($dec{0} == 'o') { 21047 // overline 21048 $dom[$key]['fontstyle'] .= 'O'; 21049 } 21050 } 21051 } 21052 } elseif ($dom[$key]['value'] == 'a') { 21053 $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle; 21054 } 21055 // check for width attribute 21056 if (isset($dom[$key]['style']['width'])) { 21057 $dom[$key]['width'] = $dom[$key]['style']['width']; 21058 } 21059 // check for height attribute 21060 if (isset($dom[$key]['style']['height'])) { 21061 $dom[$key]['height'] = $dom[$key]['style']['height']; 21062 } 21063 // check for text alignment 21064 if (isset($dom[$key]['style']['text-align'])) { 21065 $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align']{0}); 21066 } 21067 // check for CSS border properties 21068 if (isset($dom[$key]['style']['border'])) { 21069 $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border']); 21070 if (!empty($borderstyle)) { 21071 $dom[$key]['border']['LTRB'] = $borderstyle; 21072 } 21073 } 21074 if (isset($dom[$key]['style']['border-color'])) { 21075 $brd_colors = preg_split('/[\s]+/', trim($dom[$key]['style']['border-color'])); 21076 if (isset($brd_colors[3])) { 21077 $dom[$key]['border']['L']['color'] = $this->convertHTMLColorToDec($brd_colors[3]); 21078 } 21079 if (isset($brd_colors[1])) { 21080 $dom[$key]['border']['R']['color'] = $this->convertHTMLColorToDec($brd_colors[1]); 21081 } 21082 if (isset($brd_colors[0])) { 21083 $dom[$key]['border']['T']['color'] = $this->convertHTMLColorToDec($brd_colors[0]); 21084 } 21085 if (isset($brd_colors[2])) { 21086 $dom[$key]['border']['B']['color'] = $this->convertHTMLColorToDec($brd_colors[2]); 21087 } 21088 } 21089 if (isset($dom[$key]['style']['border-width'])) { 21090 $brd_widths = preg_split('/[\s]+/', trim($dom[$key]['style']['border-width'])); 21091 if (isset($brd_widths[3])) { 21092 $dom[$key]['border']['L']['width'] = $this->getCSSBorderWidth($brd_widths[3]); 21093 } 21094 if (isset($brd_widths[1])) { 21095 $dom[$key]['border']['R']['width'] = $this->getCSSBorderWidth($brd_widths[1]); 21096 } 21097 if (isset($brd_widths[0])) { 21098 $dom[$key]['border']['T']['width'] = $this->getCSSBorderWidth($brd_widths[0]); 21099 } 21100 if (isset($brd_widths[2])) { 21101 $dom[$key]['border']['B']['width'] = $this->getCSSBorderWidth($brd_widths[2]); 21102 } 21103 } 21104 if (isset($dom[$key]['style']['border-style'])) { 21105 $brd_styles = preg_split('/[\s]+/', trim($dom[$key]['style']['border-style'])); 21106 if (isset($brd_styles[3])) { 21107 $dom[$key]['border']['L']['cap'] = 'square'; 21108 $dom[$key]['border']['L']['join'] = 'miter'; 21109 $dom[$key]['border']['L']['dash'] = $this->getCSSBorderDashStyle($brd_styles[3]); 21110 if ($dom[$key]['border']['L']['dash'] < 0) { 21111 $dom[$key]['border']['L'] = array(); 21112 } 21113 } 21114 if (isset($brd_styles[1])) { 21115 $dom[$key]['border']['R']['cap'] = 'square'; 21116 $dom[$key]['border']['R']['join'] = 'miter'; 21117 $dom[$key]['border']['R']['dash'] = $this->getCSSBorderDashStyle($brd_styles[1]); 21118 if ($dom[$key]['border']['R']['dash'] < 0) { 21119 $dom[$key]['border']['R'] = array(); 21120 } 21121 } 21122 if (isset($brd_styles[0])) { 21123 $dom[$key]['border']['T']['cap'] = 'square'; 21124 $dom[$key]['border']['T']['join'] = 'miter'; 21125 $dom[$key]['border']['T']['dash'] = $this->getCSSBorderDashStyle($brd_styles[0]); 21126 if ($dom[$key]['border']['T']['dash'] < 0) { 21127 $dom[$key]['border']['T'] = array(); 21128 } 21129 } 21130 if (isset($brd_styles[2])) { 21131 $dom[$key]['border']['B']['cap'] = 'square'; 21132 $dom[$key]['border']['B']['join'] = 'miter'; 21133 $dom[$key]['border']['B']['dash'] = $this->getCSSBorderDashStyle($brd_styles[2]); 21134 if ($dom[$key]['border']['B']['dash'] < 0) { 21135 $dom[$key]['border']['B'] = array(); 21136 } 21137 } 21138 } 21139 $cellside = array('L' => 'left', 'R' => 'right', 'T' => 'top', 'B' => 'bottom'); 21140 foreach ($cellside as $bsk => $bsv) { 21141 if (isset($dom[$key]['style']['border-'.$bsv])) { 21142 $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border-'.$bsv]); 21143 if (!empty($borderstyle)) { 21144 $dom[$key]['border'][$bsk] = $borderstyle; 21145 } 21146 } 21147 if (isset($dom[$key]['style']['border-'.$bsv.'-color'])) { 21148 $dom[$key]['border'][$bsk]['color'] = $this->convertHTMLColorToDec($dom[$key]['style']['border-'.$bsv.'-color']); 21149 } 21150 if (isset($dom[$key]['style']['border-'.$bsv.'-width'])) { 21151 $dom[$key]['border'][$bsk]['width'] = $this->getCSSBorderWidth($dom[$key]['style']['border-'.$bsv.'-width']); 21152 } 21153 if (isset($dom[$key]['style']['border-'.$bsv.'-style'])) { 21154 $dom[$key]['border'][$bsk]['dash'] = $this->getCSSBorderDashStyle($dom[$key]['style']['border-'.$bsv.'-style']); 21155 if ($dom[$key]['border'][$bsk]['dash'] < 0) { 21156 $dom[$key]['border'][$bsk] = array(); 21157 } 21158 } 21159 } 21160 // check for CSS padding properties 21161 if (isset($dom[$key]['style']['padding'])) { 21162 $dom[$key]['padding'] = $this->getCSSPadding($dom[$key]['style']['padding']); 21163 } else { 21164 $dom[$key]['padding'] = $this->cell_padding; 21165 } 21166 foreach ($cellside as $psk => $psv) { 21167 if (isset($dom[$key]['style']['padding-'.$psv])) { 21168 $dom[$key]['padding'][$psk] = $this->getHTMLUnitToUnits($dom[$key]['style']['padding-'.$psv], 0, 'px', false); 21169 } 21170 } 21171 // check for CSS margin properties 21172 if (isset($dom[$key]['style']['margin'])) { 21173 $dom[$key]['margin'] = $this->getCSSMargin($dom[$key]['style']['margin']); 21174 } else { 21175 $dom[$key]['margin'] = $this->cell_margin; 21176 } 21177 foreach ($cellside as $psk => $psv) { 21178 if (isset($dom[$key]['style']['margin-'.$psv])) { 21179 $dom[$key]['margin'][$psk] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $dom[$key]['style']['margin-'.$psv]), 0, 'px', false); 21180 } 21181 } 21182 // check for CSS border-spacing properties 21183 if (isset($dom[$key]['style']['border-spacing'])) { 21184 $dom[$key]['border-spacing'] = $this->getCSSBorderMargin($dom[$key]['style']['border-spacing']); 21185 } 21186 // page-break-inside 21187 if (isset($dom[$key]['style']['page-break-inside']) AND ($dom[$key]['style']['page-break-inside'] == 'avoid')) { 21188 $dom[$key]['attribute']['nobr'] = 'true'; 21189 } 21190 // page-break-before 21191 if (isset($dom[$key]['style']['page-break-before'])) { 21192 if ($dom[$key]['style']['page-break-before'] == 'always') { 21193 $dom[$key]['attribute']['pagebreak'] = 'true'; 21194 } elseif ($dom[$key]['style']['page-break-before'] == 'left') { 21195 $dom[$key]['attribute']['pagebreak'] = 'left'; 21196 } elseif ($dom[$key]['style']['page-break-before'] == 'right') { 21197 $dom[$key]['attribute']['pagebreak'] = 'right'; 21198 } 21199 } 21200 // page-break-after 21201 if (isset($dom[$key]['style']['page-break-after'])) { 21202 if ($dom[$key]['style']['page-break-after'] == 'always') { 21203 $dom[$key]['attribute']['pagebreakafter'] = 'true'; 21204 } elseif ($dom[$key]['style']['page-break-after'] == 'left') { 21205 $dom[$key]['attribute']['pagebreakafter'] = 'left'; 21206 } elseif ($dom[$key]['style']['page-break-after'] == 'right') { 21207 $dom[$key]['attribute']['pagebreakafter'] = 'right'; 21208 } 21209 } 21210 } 21211 if (isset($dom[$key]['attribute']['display'])) { 21212 $dom[$key]['hide'] = (trim(strtolower($dom[$key]['attribute']['display'])) == 'none'); 21213 } 21214 if (isset($dom[$key]['attribute']['border']) AND ($dom[$key]['attribute']['border'] != 0)) { 21215 $borderstyle = $this->getCSSBorderStyle($dom[$key]['attribute']['border'].' solid black'); 21216 if (!empty($borderstyle)) { 21217 $dom[$key]['border']['LTRB'] = $borderstyle; 21218 } 21219 } 21220 // check for font tag 21221 if ($dom[$key]['value'] == 'font') { 21222 // font family 21223 if (isset($dom[$key]['attribute']['face'])) { 21224 $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['attribute']['face']); 21225 } 21226 // font size 21227 if (isset($dom[$key]['attribute']['size'])) { 21228 if ($key > 0) { 21229 if ($dom[$key]['attribute']['size']{0} == '+') { 21230 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1)); 21231 } elseif ($dom[$key]['attribute']['size']{0} == '-') { 21232 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1)); 21233 } else { 21234 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']); 21235 } 21236 } else { 21237 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']); 21238 } 21239 } 21240 } 21241 // force natural alignment for lists 21242 if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl')) 21243 AND (!isset($dom[$key]['align']) OR $this->empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) { 21244 if ($this->rtl) { 21245 $dom[$key]['align'] = 'R'; 21246 } else { 21247 $dom[$key]['align'] = 'L'; 21248 } 21249 } 21250 if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) { 21251 if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) { 21252 $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO; 21253 } 21254 } 21255 if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) { 21256 $dom[$key]['fontstyle'] .= 'B'; 21257 } 21258 if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) { 21259 $dom[$key]['fontstyle'] .= 'I'; 21260 } 21261 if ($dom[$key]['value'] == 'u') { 21262 $dom[$key]['fontstyle'] .= 'U'; 21263 } 21264 if (($dom[$key]['value'] == 'del') OR ($dom[$key]['value'] == 's') OR ($dom[$key]['value'] == 'strike')) { 21265 $dom[$key]['fontstyle'] .= 'D'; 21266 } 21267 if (!isset($dom[$key]['style']['text-decoration']) AND ($dom[$key]['value'] == 'a')) { 21268 $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle; 21269 } 21270 if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) { 21271 $dom[$key]['fontname'] = $this->default_monospaced_font; 21272 } 21273 if (($dom[$key]['value']{0} == 'h') AND (intval($dom[$key]['value']{1}) > 0) AND (intval($dom[$key]['value']{1}) < 7)) { 21274 // headings h1, h2, h3, h4, h5, h6 21275 if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) { 21276 $headsize = (4 - intval($dom[$key]['value']{1})) * 2; 21277 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize; 21278 } 21279 if (!isset($dom[$key]['style']['font-weight'])) { 21280 $dom[$key]['fontstyle'] .= 'B'; 21281 } 21282 } 21283 if (($dom[$key]['value'] == 'table')) { 21284 $dom[$key]['rows'] = 0; // number of rows 21285 $dom[$key]['trids'] = array(); // IDs of TR elements 21286 $dom[$key]['thead'] = ''; // table header rows 21287 } 21288 if (($dom[$key]['value'] == 'tr')) { 21289 $dom[$key]['cols'] = 0; 21290 if ($thead) { 21291 $dom[$key]['thead'] = true; 21292 // rows on thead block are printed as a separate table 21293 } else { 21294 $dom[$key]['thead'] = false; 21295 // store the number of rows on table element 21296 ++$dom[($dom[$key]['parent'])]['rows']; 21297 // store the TR elements IDs on table element 21298 array_push($dom[($dom[$key]['parent'])]['trids'], $key); 21299 } 21300 } 21301 if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) { 21302 if (isset($dom[$key]['attribute']['colspan'])) { 21303 $colspan = intval($dom[$key]['attribute']['colspan']); 21304 } else { 21305 $colspan = 1; 21306 } 21307 $dom[$key]['attribute']['colspan'] = $colspan; 21308 $dom[($dom[$key]['parent'])]['cols'] += $colspan; 21309 } 21310 // text direction 21311 if (isset($dom[$key]['attribute']['dir'])) { 21312 $dom[$key]['dir'] = $dom[$key]['attribute']['dir']; 21313 } 21314 // set foreground color attribute 21315 if (isset($dom[$key]['attribute']['color']) AND (!$this->empty_string($dom[$key]['attribute']['color']))) { 21316 $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['color']); 21317 } elseif (!isset($dom[$key]['style']['color']) AND ($dom[$key]['value'] == 'a')) { 21318 $dom[$key]['fgcolor'] = $this->htmlLinkColorArray; 21319 } 21320 // set background color attribute 21321 if (isset($dom[$key]['attribute']['bgcolor']) AND (!$this->empty_string($dom[$key]['attribute']['bgcolor']))) { 21322 $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['bgcolor']); 21323 } 21324 // set stroke color attribute 21325 if (isset($dom[$key]['attribute']['strokecolor']) AND (!$this->empty_string($dom[$key]['attribute']['strokecolor']))) { 21326 $dom[$key]['strokecolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['strokecolor']); 21327 } 21328 // check for width attribute 21329 if (isset($dom[$key]['attribute']['width'])) { 21330 $dom[$key]['width'] = $dom[$key]['attribute']['width']; 21331 } 21332 // check for height attribute 21333 if (isset($dom[$key]['attribute']['height'])) { 21334 $dom[$key]['height'] = $dom[$key]['attribute']['height']; 21335 } 21336 // check for text alignment 21337 if (isset($dom[$key]['attribute']['align']) AND (!$this->empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) { 21338 $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align']{0}); 21339 } 21340 // check for text rendering mode (the following attributes do not exist in HTML) 21341 if (isset($dom[$key]['attribute']['stroke'])) { 21342 // font stroke width 21343 $dom[$key]['stroke'] = $this->getHTMLUnitToUnits($dom[$key]['attribute']['stroke'], $dom[$key]['fontsize'], 'pt', true); 21344 } 21345 if (isset($dom[$key]['attribute']['fill'])) { 21346 // font fill 21347 if ($dom[$key]['attribute']['fill'] == 'true') { 21348 $dom[$key]['fill'] = true; 21349 } else { 21350 $dom[$key]['fill'] = false; 21351 } 21352 } 21353 if (isset($dom[$key]['attribute']['clip'])) { 21354 // clipping mode 21355 if ($dom[$key]['attribute']['clip'] == 'true') { 21356 $dom[$key]['clip'] = true; 21357 } else { 21358 $dom[$key]['clip'] = false; 21359 } 21360 } 21361 } // end opening tag 21362 } else { 21363 // text 21364 $dom[$key]['tag'] = false; 21365 $dom[$key]['block'] = false; 21366 //$element = str_replace(' ', $this->unichr(160), $element); 21367 $dom[$key]['value'] = stripslashes($this->unhtmlentities($element)); 21368 $dom[$key]['parent'] = end($level); 21369 $dom[$key]['dir'] = $dom[$dom[$key]['parent']]['dir']; 21370 } 21371 ++$elkey; 21372 ++$key; 21373 } 21374 return $dom; 21375 } 21376 21384 protected function getSpaceString() { 21385 $spacestr = chr(32); 21386 if ($this->isUnicodeFont()) { 21387 $spacestr = chr(0).chr(32); 21388 } 21389 return $spacestr; 21390 } 21391 21411 public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=false, $reseth=true, $align='', $autopadding=true) { 21412 return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true, $autopadding, 0, 'T', false); 21413 } 21414 21427 public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') { 21428 $gvars = $this->getGraphicVars(); 21429 // store current values 21430 $prev_cell_margin = $this->cell_margin; 21431 $prev_cell_padding = $this->cell_padding; 21432 $prevPage = $this->page; 21433 $prevlMargin = $this->lMargin; 21434 $prevrMargin = $this->rMargin; 21435 $curfontname = $this->FontFamily; 21436 $curfontstyle = $this->FontStyle; 21437 $curfontsize = $this->FontSizePt; 21438 $curfontascent = $this->getFontAscent($curfontname, $curfontstyle, $curfontsize); 21439 $curfontdescent = $this->getFontDescent($curfontname, $curfontstyle, $curfontsize); 21440 $curfontstretcing = $this->font_stretching; 21441 $curfontkerning = $this->font_spacing; 21442 $this->newline = true; 21443 $newline = true; 21444 $startlinepage = $this->page; 21445 $minstartliney = $this->y; 21446 $maxbottomliney = 0; 21447 $startlinex = $this->x; 21448 $startliney = $this->y; 21449 $yshift = 0; 21450 $loop = 0; 21451 $curpos = 0; 21452 $this_method_vars = array(); 21453 $undo = false; 21454 $fontaligned = false; 21455 $reverse_dir = false; // true when the text direction is reversed 21456 $this->premode = false; 21457 if ($this->inxobj) { 21458 // we are inside an XObject template 21459 $pask = count($this->xobjects[$this->xobjid]['annotations']); 21460 } elseif (isset($this->PageAnnots[$this->page])) { 21461 $pask = count($this->PageAnnots[$this->page]); 21462 } else { 21463 $pask = 0; 21464 } 21465 if ($this->inxobj) { 21466 // we are inside an XObject template 21467 $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']); 21468 } elseif (!$this->InFooter) { 21469 if (isset($this->footerlen[$this->page])) { 21470 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page]; 21471 } else { 21472 $this->footerpos[$this->page] = $this->pagelen[$this->page]; 21473 } 21474 $startlinepos = $this->footerpos[$this->page]; 21475 } else { 21476 // we are inside the footer 21477 $startlinepos = $this->pagelen[$this->page]; 21478 } 21479 $lalign = $align; 21480 $plalign = $align; 21481 if ($this->rtl) { 21482 $w = $this->x - $this->lMargin; 21483 } else { 21484 $w = $this->w - $this->rMargin - $this->x; 21485 } 21486 $w -= ($this->cell_padding['L'] + $this->cell_padding['R']); 21487 if ($cell) { 21488 if ($this->rtl) { 21489 $this->x -= $this->cell_padding['R']; 21490 $this->lMargin += $this->cell_padding['R']; 21491 } else { 21492 $this->x += $this->cell_padding['L']; 21493 $this->rMargin += $this->cell_padding['L']; 21494 } 21495 } 21496 if ($this->customlistindent >= 0) { 21497 $this->listindent = $this->customlistindent; 21498 } else { 21499 $this->listindent = $this->GetStringWidth('000000'); 21500 } 21501 $this->listindentlevel = 0; 21502 // save previous states 21503 $prev_cell_height_ratio = $this->cell_height_ratio; 21504 $prev_listnum = $this->listnum; 21505 $prev_listordered = $this->listordered; 21506 $prev_listcount = $this->listcount; 21507 $prev_lispacer = $this->lispacer; 21508 $this->listnum = 0; 21509 $this->listordered = array(); 21510 $this->listcount = array(); 21511 $this->lispacer = ''; 21512 if (($this->empty_string($this->lasth)) OR ($reseth)) { 21513 // reset row height 21514 $this->resetLastH(); 21515 } 21516 $dom = $this->getHtmlDomArray($html); 21517 $maxel = count($dom); 21518 $key = 0; 21519 $hidden_node_key = -1; 21520 while ($key < $maxel) { 21521 if ($dom[$key]['tag']) { 21522 if ($dom[$key]['opening']) { 21523 if (($hidden_node_key <= 0) AND $dom[$key]['hide']) { 21524 // store the node key 21525 $hidden_node_key = $key; 21526 } 21527 } elseif (($hidden_node_key > 0) AND ($dom[$key]['parent'] == $hidden_node_key)) { 21528 // we have reached the closing tag of the hidden node 21529 $hidden_node_key = 0; 21530 } 21531 } 21532 if ($hidden_node_key >= 0) { 21533 // skip this node 21534 ++$key; 21535 if ($hidden_node_key == 0) { 21536 // reset hidden mode 21537 $hidden_node_key = -1; 21538 } 21539 continue; 21540 } 21541 if ($dom[$key]['tag'] AND isset($dom[$key]['attribute']['pagebreak'])) { 21542 // check for pagebreak 21543 if (($dom[$key]['attribute']['pagebreak'] == 'true') OR ($dom[$key]['attribute']['pagebreak'] == 'left') OR ($dom[$key]['attribute']['pagebreak'] == 'right')) { 21544 // add a page (or trig AcceptPageBreak() for multicolumn mode) 21545 $this->checkPageBreak($this->PageBreakTrigger + 1); 21546 } 21547 if ((($dom[$key]['attribute']['pagebreak'] == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0)))) 21548 OR (($dom[$key]['attribute']['pagebreak'] == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) { 21549 // add a page (or trig AcceptPageBreak() for multicolumn mode) 21550 $this->checkPageBreak($this->PageBreakTrigger + 1); 21551 } 21552 } 21553 if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND isset($dom[$key]['attribute']['nobr']) AND ($dom[$key]['attribute']['nobr'] == 'true')) { 21554 if (isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) { 21555 $dom[$key]['attribute']['nobr'] = false; 21556 } else { 21557 // store current object 21558 $this->startTransaction(); 21559 // save this method vars 21560 $this_method_vars['html'] = $html; 21561 $this_method_vars['ln'] = $ln; 21562 $this_method_vars['fill'] = $fill; 21563 $this_method_vars['reseth'] = $reseth; 21564 $this_method_vars['cell'] = $cell; 21565 $this_method_vars['align'] = $align; 21566 $this_method_vars['gvars'] = $gvars; 21567 $this_method_vars['prevPage'] = $prevPage; 21568 $this_method_vars['prev_cell_margin'] = $prev_cell_margin; 21569 $this_method_vars['prev_cell_padding'] = $prev_cell_padding; 21570 $this_method_vars['prevlMargin'] = $prevlMargin; 21571 $this_method_vars['prevrMargin'] = $prevrMargin; 21572 $this_method_vars['curfontname'] = $curfontname; 21573 $this_method_vars['curfontstyle'] = $curfontstyle; 21574 $this_method_vars['curfontsize'] = $curfontsize; 21575 $this_method_vars['curfontascent'] = $curfontascent; 21576 $this_method_vars['curfontdescent'] = $curfontdescent; 21577 $this_method_vars['curfontstretcing'] = $curfontstretcing; 21578 $this_method_vars['curfontkerning'] = $curfontkerning; 21579 $this_method_vars['minstartliney'] = $minstartliney; 21580 $this_method_vars['maxbottomliney'] = $maxbottomliney; 21581 $this_method_vars['yshift'] = $yshift; 21582 $this_method_vars['startlinepage'] = $startlinepage; 21583 $this_method_vars['startlinepos'] = $startlinepos; 21584 $this_method_vars['startlinex'] = $startlinex; 21585 $this_method_vars['startliney'] = $startliney; 21586 $this_method_vars['newline'] = $newline; 21587 $this_method_vars['loop'] = $loop; 21588 $this_method_vars['curpos'] = $curpos; 21589 $this_method_vars['pask'] = $pask; 21590 $this_method_vars['lalign'] = $lalign; 21591 $this_method_vars['plalign'] = $plalign; 21592 $this_method_vars['w'] = $w; 21593 $this_method_vars['prev_cell_height_ratio'] = $prev_cell_height_ratio; 21594 $this_method_vars['prev_listnum'] = $prev_listnum; 21595 $this_method_vars['prev_listordered'] = $prev_listordered; 21596 $this_method_vars['prev_listcount'] = $prev_listcount; 21597 $this_method_vars['prev_lispacer'] = $prev_lispacer; 21598 $this_method_vars['fontaligned'] = $fontaligned; 21599 $this_method_vars['key'] = $key; 21600 $this_method_vars['dom'] = $dom; 21601 } 21602 } 21603 // print THEAD block 21604 if (($dom[$key]['value'] == 'tr') AND isset($dom[$key]['thead']) AND $dom[$key]['thead']) { 21605 if (isset($dom[$key]['parent']) AND isset($dom[$dom[$key]['parent']]['thead']) AND !$this->empty_string($dom[$dom[$key]['parent']]['thead'])) { 21606 $this->inthead = true; 21607 // print table header (thead) 21608 $this->writeHTML($this->thead, false, false, false, false, ''); 21609 // check if we are on a new page or on a new column 21610 if (($this->y < $this->start_transaction_y) OR ($this->checkPageBreak($this->lasth, '', false))) { 21611 // we are on a new page or on a new column and the total object height is less than the available vertical space. 21612 // restore previous object 21613 $this->rollbackTransaction(true); 21614 // restore previous values 21615 foreach ($this_method_vars as $vkey => $vval) { 21616 $$vkey = $vval; 21617 } 21618 // disable table header 21619 $tmp_thead = $this->thead; 21620 $this->thead = ''; 21621 // add a page (or trig AcceptPageBreak() for multicolumn mode) 21622 $pre_y = $this->y; 21623 if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) { 21624 // fix for multicolumn mode 21625 $startliney = $this->y; 21626 } 21627 $this->start_transaction_page = $this->page; 21628 $this->start_transaction_y = $this->y; 21629 // restore table header 21630 $this->thead = $tmp_thead; 21631 // fix table border properties 21632 if (isset($dom[$dom[$key]['parent']]['attribute']['cellspacing'])) { 21633 $tmp_cellspacing = $this->getHTMLUnitToUnits($dom[$dom[$key]['parent']]['attribute']['cellspacing'], 1, 'px'); 21634 } elseif (isset($dom[$dom[$key]['parent']]['border-spacing'])) { 21635 $tmp_cellspacing = $dom[$dom[$key]['parent']]['border-spacing']['V']; 21636 } else { 21637 $tmp_cellspacing = 0; 21638 } 21639 $dom[$dom[$key]['parent']]['borderposition']['page'] = $this->page; 21640 $dom[$dom[$key]['parent']]['borderposition']['column'] = $this->current_column; 21641 $dom[$dom[$key]['parent']]['borderposition']['y'] = $this->y + $tmp_cellspacing; 21642 $xoffset = ($this->x - $dom[$dom[$key]['parent']]['borderposition']['x']); 21643 $dom[$dom[$key]['parent']]['borderposition']['x'] += $xoffset; 21644 $dom[$dom[$key]['parent']]['borderposition']['xmax'] += $xoffset; 21645 // print table header (thead) 21646 $this->writeHTML($this->thead, false, false, false, false, ''); 21647 } 21648 } 21649 // move $key index forward to skip THEAD block 21650 while ( ($key < $maxel) AND (!( 21651 ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'tr') AND (!isset($dom[$key]['thead']) OR !$dom[$key]['thead'])) 21652 OR ($dom[$key]['tag'] AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == 'table'))) )) { 21653 ++$key; 21654 } 21655 } 21656 if ($dom[$key]['tag'] OR ($key == 0)) { 21657 if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) { 21658 $dom[$key]['align'] = ($this->rtl) ? 'R' : 'L'; 21659 } 21660 // vertically align image in line 21661 if ((!$this->newline) AND ($dom[$key]['value'] == 'img') AND (isset($dom[$key]['height'])) AND ($dom[$key]['height'] > 0)) { 21662 // get image height 21663 $imgh = $this->getHTMLUnitToUnits($dom[$key]['height'], $this->lasth, 'px'); 21664 $autolinebreak = false; 21665 if (isset($dom[$key]['width']) AND ($dom[$key]['width'] > 0)) { 21666 $imgw = $this->getHTMLUnitToUnits($dom[$key]['width'], 1, 'px', false); 21667 if (($imgw <= ($this->w - $this->lMargin - $this->rMargin - $this->cell_padding['L'] - $this->cell_padding['R'])) 21668 AND ((($this->rtl) AND (($this->x - $imgw) < ($this->lMargin + $this->cell_padding['L']))) 21669 OR ((!$this->rtl) AND (($this->x + $imgw) > ($this->w - $this->rMargin - $this->cell_padding['R']))))) { 21670 // add automatic line break 21671 $autolinebreak = true; 21672 $this->Ln('', $cell); 21673 if ((!$dom[($key-1)]['tag']) AND ($dom[($key-1)]['value'] == ' ')) { 21674 // go back to evaluate this line break 21675 --$key; 21676 } 21677 } 21678 } 21679 if (!$autolinebreak) { 21680 if ($this->inPageBody()) { 21681 $pre_y = $this->y; 21682 // check for page break 21683 if ((!$this->checkPageBreak($imgh)) AND ($this->y < $pre_y)) { 21684 // fix for multicolumn mode 21685 $startliney = $this->y; 21686 } 21687 } 21688 if ($this->page > $startlinepage) { 21689 // fix line splitted over two pages 21690 if (isset($this->footerlen[$startlinepage])) { 21691 $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 21692 } 21693 // line to be moved one page forward 21694 $pagebuff = $this->getPageBuffer($startlinepage); 21695 $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos)); 21696 $tstart = substr($pagebuff, 0, $startlinepos); 21697 $tend = substr($this->getPageBuffer($startlinepage), $curpos); 21698 // remove line from previous page 21699 $this->setPageBuffer($startlinepage, $tstart.''.$tend); 21700 $pagebuff = $this->getPageBuffer($this->page); 21701 $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]); 21702 $tend = substr($pagebuff, $this->cntmrk[$this->page]); 21703 // add line start to current page 21704 $yshift = ($minstartliney - $this->y); 21705 if ($fontaligned) { 21706 $yshift += ($curfontsize / $this->k); 21707 } 21708 $try = sprintf('1 0 0 1 0 %.3F cm', ($yshift * $this->k)); 21709 $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend); 21710 // shift the annotations and links 21711 if (isset($this->PageAnnots[$this->page])) { 21712 $next_pask = count($this->PageAnnots[$this->page]); 21713 } else { 21714 $next_pask = 0; 21715 } 21716 if (isset($this->PageAnnots[$startlinepage])) { 21717 foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) { 21718 if ($pak >= $pask) { 21719 $this->PageAnnots[$this->page][] = $pac; 21720 unset($this->PageAnnots[$startlinepage][$pak]); 21721 $npak = count($this->PageAnnots[$this->page]) - 1; 21722 $this->PageAnnots[$this->page][$npak]['y'] -= $yshift; 21723 } 21724 } 21725 } 21726 $pask = $next_pask; 21727 $startlinepos = $this->cntmrk[$this->page]; 21728 $startlinepage = $this->page; 21729 $startliney = $this->y; 21730 $this->newline = false; 21731 } 21732 $this->y += ((($curfontsize * $this->cell_height_ratio / $this->k) + $curfontascent - $curfontdescent) / 2) - $imgh; 21733 $minstartliney = min($this->y, $minstartliney); 21734 $maxbottomliney = ($startliney + ($this->FontSize * $this->cell_height_ratio)); 21735 } 21736 } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize']) OR isset($dom[$key]['line-height'])) { 21737 // account for different font size 21738 $pfontname = $curfontname; 21739 $pfontstyle = $curfontstyle; 21740 $pfontsize = $curfontsize; 21741 $fontname = isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname; 21742 $fontstyle = isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle; 21743 $fontsize = isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize; 21744 $fontascent = $this->getFontAscent($fontname, $fontstyle, $fontsize); 21745 $fontdescent = $this->getFontDescent($fontname, $fontstyle, $fontsize); 21746 if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize) 21747 OR ($this->cell_height_ratio != $dom[$key]['line-height']) 21748 OR ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li')) ) { 21749 if (($key < ($maxel - 1)) AND ( 21750 ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li')) 21751 OR ($this->cell_height_ratio != $dom[$key]['line-height']) 21752 OR (!$this->newline AND is_numeric($fontsize) AND is_numeric($curfontsize) AND ($fontsize >= 0) AND ($curfontsize >= 0) AND ($fontsize != $curfontsize)) 21753 )) { 21754 if ($this->page > $startlinepage) { 21755 // fix lines splitted over two pages 21756 if (isset($this->footerlen[$startlinepage])) { 21757 $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 21758 } 21759 // line to be moved one page forward 21760 $pagebuff = $this->getPageBuffer($startlinepage); 21761 $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos)); 21762 $tstart = substr($pagebuff, 0, $startlinepos); 21763 $tend = substr($this->getPageBuffer($startlinepage), $curpos); 21764 // remove line start from previous page 21765 $this->setPageBuffer($startlinepage, $tstart.''.$tend); 21766 $pagebuff = $this->getPageBuffer($this->page); 21767 $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]); 21768 $tend = substr($pagebuff, $this->cntmrk[$this->page]); 21769 // add line start to current page 21770 $yshift = ($minstartliney - $this->y); 21771 $try = sprintf('1 0 0 1 0 %.3F cm', ($yshift * $this->k)); 21772 $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend); 21773 // shift the annotations and links 21774 if (isset($this->PageAnnots[$this->page])) { 21775 $next_pask = count($this->PageAnnots[$this->page]); 21776 } else { 21777 $next_pask = 0; 21778 } 21779 if (isset($this->PageAnnots[$startlinepage])) { 21780 foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) { 21781 if ($pak >= $pask) { 21782 $this->PageAnnots[$this->page][] = $pac; 21783 unset($this->PageAnnots[$startlinepage][$pak]); 21784 $npak = count($this->PageAnnots[$this->page]) - 1; 21785 $this->PageAnnots[$this->page][$npak]['y'] -= $yshift; 21786 } 21787 } 21788 } 21789 $pask = $next_pask; 21790 $startlinepos = $this->cntmrk[$this->page]; 21791 $startlinepage = $this->page; 21792 $startliney = $this->y; 21793 } 21794 if (!isset($dom[$key]['line-height'])) { 21795 $dom[$key]['line-height'] = $this->cell_height_ratio; 21796 } 21797 if (!$dom[$key]['block']) { 21798 if (!(isset($dom[($key + 1)]) AND $dom[($key + 1)]['tag'] AND (!$dom[($key + 1)]['opening']) AND ($dom[($key + 1)]['value'] != 'li') AND $dom[$key]['tag'] AND (!$dom[$key]['opening']))) { 21799 $this->y += (((($curfontsize * $this->cell_height_ratio) - ($fontsize * $dom[$key]['line-height'])) / $this->k) + $curfontascent - $fontascent - $curfontdescent + $fontdescent) / 2; 21800 } 21801 if (($dom[$key]['value'] != 'sup') AND ($dom[$key]['value'] != 'sub')) { 21802 $current_line_align_data = array($key, $minstartliney, $maxbottomliney); 21803 if (isset($line_align_data) AND (($line_align_data[0] == ($key - 1)) OR (($line_align_data[0] == ($key - 2)) AND (isset($dom[($key - 1)])) AND (preg_match('/^([\s]+)$/', $dom[($key - 1)]['value']) > 0)))) { 21804 $minstartliney = min($this->y, $line_align_data[1]); 21805 $maxbottomliney = max(($this->y + (($fontsize * $this->cell_height_ratio) / $this->k)), $line_align_data[2]); 21806 } else { 21807 $minstartliney = min($this->y, $minstartliney); 21808 $maxbottomliney = max(($this->y + (($fontsize * $this->cell_height_ratio) / $this->k)), $maxbottomliney); 21809 } 21810 $line_align_data = $current_line_align_data; 21811 } 21812 } 21813 $this->cell_height_ratio = $dom[$key]['line-height']; 21814 $fontaligned = true; 21815 } 21816 $this->SetFont($fontname, $fontstyle, $fontsize); 21817 // reset row height 21818 $this->resetLastH(); 21819 $curfontname = $fontname; 21820 $curfontstyle = $fontstyle; 21821 $curfontsize = $fontsize; 21822 $curfontascent = $fontascent; 21823 $curfontdescent = $fontdescent; 21824 } 21825 } 21826 // set text rendering mode 21827 $textstroke = isset($dom[$key]['stroke']) ? $dom[$key]['stroke'] : $this->textstrokewidth; 21828 $textfill = isset($dom[$key]['fill']) ? $dom[$key]['fill'] : (($this->textrendermode % 2) == 0); 21829 $textclip = isset($dom[$key]['clip']) ? $dom[$key]['clip'] : ($this->textrendermode > 3); 21830 $this->setTextRenderingMode($textstroke, $textfill, $textclip); 21831 if (isset($dom[$key]['font-stretch']) AND ($dom[$key]['font-stretch'] !== false)) { 21832 $this->setFontStretching($dom[$key]['font-stretch']); 21833 } 21834 if (isset($dom[$key]['letter-spacing']) AND ($dom[$key]['letter-spacing'] !== false)) { 21835 $this->setFontSpacing($dom[$key]['letter-spacing']); 21836 } 21837 if (($plalign == 'J') AND $dom[$key]['block']) { 21838 $plalign = ''; 21839 } 21840 // get current position on page buffer 21841 $curpos = $this->pagelen[$startlinepage]; 21842 if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) { 21843 $this->SetFillColorArray($dom[$key]['bgcolor']); 21844 $wfill = true; 21845 } else { 21846 $wfill = $fill | false; 21847 } 21848 if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) { 21849 $this->SetTextColorArray($dom[$key]['fgcolor']); 21850 } 21851 if (isset($dom[$key]['strokecolor']) AND ($dom[$key]['strokecolor'] !== false)) { 21852 $this->SetDrawColorArray($dom[$key]['strokecolor']); 21853 } 21854 if (isset($dom[$key]['align'])) { 21855 $lalign = $dom[$key]['align']; 21856 } 21857 if ($this->empty_string($lalign)) { 21858 $lalign = $align; 21859 } 21860 } 21861 // align lines 21862 if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) { 21863 $newline = true; 21864 $fontaligned = false; 21865 // we are at the beginning of a new line 21866 if (isset($startlinex)) { 21867 $yshift = ($minstartliney - $startliney); 21868 if (($yshift > 0) OR ($this->page > $startlinepage)) { 21869 $yshift = 0; 21870 } 21871 $t_x = 0; 21872 // the last line must be shifted to be aligned as requested 21873 $linew = abs($this->endlinex - $startlinex); 21874 if ($this->inxobj) { 21875 // we are inside an XObject template 21876 $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos); 21877 if (isset($opentagpos)) { 21878 $midpos = $opentagpos; 21879 } else { 21880 $midpos = 0; 21881 } 21882 if ($midpos > 0) { 21883 $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos)); 21884 $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos); 21885 } else { 21886 $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos); 21887 $pend = ''; 21888 } 21889 } else { 21890 $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos); 21891 if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) { 21892 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 21893 $midpos = min($opentagpos, $this->footerpos[$startlinepage]); 21894 } elseif (isset($opentagpos)) { 21895 $midpos = $opentagpos; 21896 } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) { 21897 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 21898 $midpos = $this->footerpos[$startlinepage]; 21899 } else { 21900 $midpos = 0; 21901 } 21902 if ($midpos > 0) { 21903 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos)); 21904 $pend = substr($this->getPageBuffer($startlinepage), $midpos); 21905 } else { 21906 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos); 21907 $pend = ''; 21908 } 21909 } 21910 if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) { 21911 // calculate shifting amount 21912 $tw = $w; 21913 if (($plalign == 'J') AND $this->isRTLTextDir() AND ($this->num_columns > 1)) { 21914 $tw += $this->cell_padding['R']; 21915 } 21916 if ($this->lMargin != $prevlMargin) { 21917 $tw += ($prevlMargin - $this->lMargin); 21918 } 21919 if ($this->rMargin != $prevrMargin) { 21920 $tw += ($prevrMargin - $this->rMargin); 21921 } 21922 $one_space_width = $this->GetStringWidth(chr(32)); 21923 $no = 0; // number of spaces on a line contained on a single block 21924 if ($this->isRTLTextDir()) { // RTL 21925 // remove left space if exist 21926 $pos1 = $this->revstrpos($pmid, '[('); 21927 if ($pos1 > 0) { 21928 $pos1 = intval($pos1); 21929 if ($this->isUnicodeFont()) { 21930 $pos2 = intval($this->revstrpos($pmid, '[('.chr(0).chr(32))); 21931 $spacelen = 2; 21932 } else { 21933 $pos2 = intval($this->revstrpos($pmid, '[('.chr(32))); 21934 $spacelen = 1; 21935 } 21936 if ($pos1 == $pos2) { 21937 $pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen)); 21938 if (substr($pmid, $pos1, 4) == '[()]') { 21939 $linew -= $one_space_width; 21940 } elseif ($pos1 == strpos($pmid, '[(')) { 21941 $no = 1; 21942 } 21943 } 21944 } 21945 } else { // LTR 21946 // remove right space if exist 21947 $pos1 = $this->revstrpos($pmid, ')]'); 21948 if ($pos1 > 0) { 21949 $pos1 = intval($pos1); 21950 if ($this->isUnicodeFont()) { 21951 $pos2 = intval($this->revstrpos($pmid, chr(0).chr(32).')]')) + 2; 21952 $spacelen = 2; 21953 } else { 21954 $pos2 = intval($this->revstrpos($pmid, chr(32).')]')) + 1; 21955 $spacelen = 1; 21956 } 21957 if ($pos1 == $pos2) { 21958 $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1); 21959 $linew -= $one_space_width; 21960 } 21961 } 21962 } 21963 $mdiff = ($tw - $linew); 21964 if ($plalign == 'C') { 21965 if ($this->rtl) { 21966 $t_x = -($mdiff / 2); 21967 } else { 21968 $t_x = ($mdiff / 2); 21969 } 21970 } elseif ($plalign == 'R') { 21971 // right alignment on LTR document 21972 $t_x = $mdiff; 21973 } elseif ($plalign == 'L') { 21974 // left alignment on RTL document 21975 $t_x = -$mdiff; 21976 } elseif (($plalign == 'J') AND ($plalign == $lalign)) { 21977 // Justification 21978 if ($this->isRTLTextDir()) { 21979 // align text on the left 21980 $t_x = -$mdiff; 21981 } 21982 $ns = 0; // number of spaces 21983 $pmidtemp = $pmid; 21984 // escape special characters 21985 $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp); 21986 $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp); 21987 // search spaces 21988 if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER)) { 21989 $spacestr = $this->getSpaceString(); 21990 $maxkk = count($lnstring[1]) - 1; 21991 for ($kk=0; $kk <= $maxkk; ++$kk) { 21992 // restore special characters 21993 $lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]); 21994 $lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]); 21995 // store number of spaces on the strings 21996 $lnstring[2][$kk] = substr_count($lnstring[1][$kk], $spacestr); 21997 // count total spaces on line 21998 $ns += $lnstring[2][$kk]; 21999 $lnstring[3][$kk] = $ns; 22000 } 22001 if ($ns == 0) { 22002 $ns = 1; 22003 } 22004 // calculate additional space to add to each existing space 22005 $spacewidth = ($mdiff / ($ns - $no)) * $this->k; 22006 $spacewidthu = -1000 * ($mdiff + (($ns + $no) * $one_space_width)) / $ns / $this->FontSize; 22007 if ($this->font_spacing != 0) { 22008 // fixed spacing mode 22009 $osw = -1000 * $this->font_spacing / $this->FontSize; 22010 $spacewidthu += $osw; 22011 } 22012 $nsmax = $ns; 22013 $ns = 0; 22014 reset($lnstring); 22015 $offset = 0; 22016 $strcount = 0; 22017 $prev_epsposbeg = 0; 22018 $textpos = 0; 22019 if ($this->isRTLTextDir()) { 22020 $textpos = $this->wPt; 22021 } 22022 global $spacew; 22023 while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE, $offset) == 1) { 22024 // check if we are inside a string section '[( ... )]' 22025 $stroffset = strpos($pmid, '[(', $offset); 22026 if (($stroffset !== false) AND ($stroffset <= $strpiece[2][1])) { 22027 // set offset to the end of string section 22028 $offset = strpos($pmid, ')]', $stroffset); 22029 while (($offset !== false) AND ($pmid{($offset - 1)} == '\\')) { 22030 $offset = strpos($pmid, ')]', ($offset + 1)); 22031 } 22032 if ($offset === false) { 22033 $this->Error('HTML Justification: malformed PDF code.'); 22034 } 22035 continue; 22036 } 22037 if ($this->isRTLTextDir()) { 22038 $spacew = ($spacewidth * ($nsmax - $ns)); 22039 } else { 22040 $spacew = ($spacewidth * $ns); 22041 } 22042 $offset = $strpiece[2][1] + strlen($strpiece[2][0]); 22043 $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, $offset); 22044 $epsposend = strpos($pmid, $this->epsmarker.'Q', $offset) + strlen($this->epsmarker.'Q'); 22045 if ((($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend)) 22046 OR (($epsposbeg === false) AND ($epsposend > 0) AND ($offset < $epsposend))) { 22047 // shift EPS images 22048 $trx = sprintf('1 0 0 1 %.3F 0 cm', $spacew); 22049 $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, ($prev_epsposbeg - 6)); 22050 $pmid_b = substr($pmid, 0, $epsposbeg); 22051 $pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg)); 22052 $pmid_e = substr($pmid, $epsposend); 22053 $pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e; 22054 $offset = $epsposend; 22055 continue; 22056 22057 } 22058 $prev_epsposbeg = $epsposbeg; 22059 $currentxpos = 0; 22060 // shift blocks of code 22061 switch ($strpiece[2][0]) { 22062 case 'Td': 22063 case 'cm': 22064 case 'm': 22065 case 'l': { 22066 // get current X position 22067 preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches); 22068 $currentxpos = $xmatches[1]; 22069 $textpos = $currentxpos; 22070 if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) { 22071 $ns = $lnstring[3][$strcount]; 22072 if ($this->isRTLTextDir()) { 22073 $spacew = ($spacewidth * ($nsmax - $ns)); 22074 } 22075 ++$strcount; 22076 } 22077 // justify block 22078 $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', 22079 create_function('$matches', 'global $spacew; 22080 $newx = sprintf("%.2F",(floatval($matches[1]) + $spacew)); 22081 return "".$newx." ".$matches[2]." x*#!#*x".$matches[3].$matches[4];'), $pmid, 1); 22082 break; 22083 } 22084 case 're': { 22085 // justify block 22086 if (!$this->empty_string($this->lispacer)) { 22087 $this->lispacer = ''; 22088 continue; 22089 } 22090 preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $xmatches); 22091 $currentxpos = $xmatches[1]; 22092 global $x_diff, $w_diff; 22093 $x_diff = 0; 22094 $w_diff = 0; 22095 if ($this->isRTLTextDir()) { // RTL 22096 if ($currentxpos < $textpos) { 22097 $x_diff = ($spacewidth * ($nsmax - $lnstring[3][$strcount])); 22098 $w_diff = ($spacewidth * $lnstring[2][$strcount]); 22099 } else { 22100 if ($strcount > 0) { 22101 $x_diff = ($spacewidth * ($nsmax - $lnstring[3][($strcount - 1)])); 22102 $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]); 22103 } 22104 } 22105 } else { // LTR 22106 if ($currentxpos > $textpos) { 22107 if ($strcount > 0) { 22108 $x_diff = ($spacewidth * $lnstring[3][($strcount - 1)]); 22109 } 22110 $w_diff = ($spacewidth * $lnstring[2][$strcount]); 22111 } else { 22112 if ($strcount > 1) { 22113 $x_diff = ($spacewidth * $lnstring[3][($strcount - 2)]); 22114 } 22115 if ($strcount > 0) { 22116 $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]); 22117 } 22118 } 22119 } 22120 $pmid = preg_replace_callback('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', 22121 create_function('$matches', 'global $x_diff, $w_diff; 22122 $newx = sprintf("%.2F",(floatval($matches[1]) + $x_diff)); 22123 $neww = sprintf("%.2F",(floatval($matches[3]) + $w_diff)); 22124 return "".$newx." ".$matches[2]." ".$neww." ".$matches[4]." x*#!#*x".$matches[5].$matches[6];'), $pmid, 1); 22125 break; 22126 } 22127 case 'c': { 22128 // get current X position 22129 preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s](c)([\s]*)/x', $pmid, $xmatches); 22130 $currentxpos = $xmatches[1]; 22131 // justify block 22132 $pmid = preg_replace_callback('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$xmatches[4].')[\s]('.$xmatches[5].')[\s]('.$strpiece[1][0].')[\s](c)([\s]*)/x', 22133 create_function('$matches', 'global $spacew; 22134 $newx1 = sprintf("%.3F",(floatval($matches[1]) + $spacew)); 22135 $newx2 = sprintf("%.3F",(floatval($matches[3]) + $spacew)); 22136 $newx3 = sprintf("%.3F",(floatval($matches[5]) + $spacew)); 22137 return "".$newx1." ".$matches[2]." ".$newx2." ".$matches[4]." ".$newx3." ".$matches[6]." x*#!#*x".$matches[7].$matches[8];'), $pmid, 1); 22138 break; 22139 } 22140 } 22141 // shift the annotations and links 22142 $cxpos = ($currentxpos / $this->k); 22143 $lmpos = ($this->lMargin + $this->cell_padding['L'] + $this->feps); 22144 if ($this->inxobj) { 22145 // we are inside an XObject template 22146 foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) { 22147 if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) { 22148 if ($cxpos > $lmpos) { 22149 $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += ($spacew / $this->k); 22150 $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k); 22151 } else { 22152 $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k); 22153 } 22154 break; 22155 } 22156 } 22157 } elseif (isset($this->PageAnnots[$this->page])) { 22158 foreach ($this->PageAnnots[$this->page] as $pak => $pac) { 22159 if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) { 22160 if ($cxpos > $lmpos) { 22161 $this->PageAnnots[$this->page][$pak]['x'] += ($spacew / $this->k); 22162 $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k); 22163 } else { 22164 $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k); 22165 } 22166 break; 22167 } 22168 } 22169 } 22170 } // end of while 22171 // remove markers 22172 $pmid = str_replace('x*#!#*x', '', $pmid); 22173 if ($this->isUnicodeFont()) { 22174 // multibyte characters 22175 $spacew = $spacewidthu; 22176 if ($this->font_stretching != 100) { 22177 // word spacing is affected by stretching 22178 $spacew /= ($this->font_stretching / 100); 22179 } 22180 $pmidtemp = $pmid; 22181 // escape special characters 22182 $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp); 22183 $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp); 22184 $pmid = preg_replace_callback("/\[\(([^\)]*)\)\]/x", 22185 create_function('$matches', 'global $spacew; 22186 $matches[1] = str_replace("#!#OP#!#", "(", $matches[1]); 22187 $matches[1] = str_replace("#!#CP#!#", ")", $matches[1]); 22188 return "[(".str_replace(chr(0).chr(32), ") ".sprintf("%.3F", $spacew)." (", $matches[1]).")]";'), $pmidtemp); 22189 if ($this->inxobj) { 22190 // we are inside an XObject template 22191 $this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\n".$pend; 22192 } else { 22193 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend); 22194 } 22195 $endlinepos = strlen($pstart."\n".$pmid."\n"); 22196 } else { 22197 // non-unicode (single-byte characters) 22198 if ($this->font_stretching != 100) { 22199 // word spacing (Tw) is affected by stretching 22200 $spacewidth /= ($this->font_stretching / 100); 22201 } 22202 $rs = sprintf('%.3F Tw', $spacewidth); 22203 $pmid = preg_replace("/\[\(/x", $rs.' [(', $pmid); 22204 if ($this->inxobj) { 22205 // we are inside an XObject template 22206 $this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend; 22207 } else { 22208 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend); 22209 } 22210 $endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n"); 22211 } 22212 } 22213 } // end of J 22214 } // end if $startlinex 22215 if (($t_x != 0) OR ($yshift < 0)) { 22216 // shift the line 22217 $trx = sprintf('1 0 0 1 %.3F %.3F cm', ($t_x * $this->k), ($yshift * $this->k)); 22218 $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n"; 22219 $endlinepos = strlen($pstart); 22220 if ($this->inxobj) { 22221 // we are inside an XObject template 22222 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend; 22223 foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) { 22224 if ($pak >= $pask) { 22225 $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x; 22226 $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift; 22227 } 22228 } 22229 } else { 22230 $this->setPageBuffer($startlinepage, $pstart.$pend); 22231 // shift the annotations and links 22232 if (isset($this->PageAnnots[$this->page])) { 22233 foreach ($this->PageAnnots[$this->page] as $pak => $pac) { 22234 if ($pak >= $pask) { 22235 $this->PageAnnots[$this->page][$pak]['x'] += $t_x; 22236 $this->PageAnnots[$this->page][$pak]['y'] -= $yshift; 22237 } 22238 } 22239 } 22240 } 22241 $this->y -= $yshift; 22242 } 22243 } 22244 $pbrk = $this->checkPageBreak($this->lasth); 22245 $this->newline = false; 22246 $startlinex = $this->x; 22247 $startliney = $this->y; 22248 if ($dom[$dom[$key]['parent']]['value'] == 'sup') { 22249 $startliney -= ((0.3 * $this->FontSizePt) / $this->k); 22250 } elseif ($dom[$dom[$key]['parent']]['value'] == 'sub') { 22251 $startliney -= (($this->FontSizePt / 0.7) / $this->k); 22252 } else { 22253 $minstartliney = $startliney; 22254 $maxbottomliney = ($this->y + (($fontsize * $this->cell_height_ratio) / $this->k)); 22255 } 22256 $startlinepage = $this->page; 22257 if (isset($endlinepos) AND (!$pbrk)) { 22258 $startlinepos = $endlinepos; 22259 } else { 22260 if ($this->inxobj) { 22261 // we are inside an XObject template 22262 $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']); 22263 } elseif (!$this->InFooter) { 22264 if (isset($this->footerlen[$this->page])) { 22265 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page]; 22266 } else { 22267 $this->footerpos[$this->page] = $this->pagelen[$this->page]; 22268 } 22269 $startlinepos = $this->footerpos[$this->page]; 22270 } else { 22271 $startlinepos = $this->pagelen[$this->page]; 22272 } 22273 } 22274 unset($endlinepos); 22275 $plalign = $lalign; 22276 if (isset($this->PageAnnots[$this->page])) { 22277 $pask = count($this->PageAnnots[$this->page]); 22278 } else { 22279 $pask = 0; 22280 } 22281 if (!($dom[$key]['tag'] AND !$dom[$key]['opening'] AND ($dom[$key]['value'] == 'table') 22282 AND (isset($this->emptypagemrk[$this->page])) 22283 AND ($this->emptypagemrk[$this->page] == $this->pagelen[$this->page]))) { 22284 $this->SetFont($fontname, $fontstyle, $fontsize); 22285 if ($wfill) { 22286 $this->SetFillColorArray($this->bgcolor); 22287 } 22288 } 22289 } // end newline 22290 if (isset($opentagpos)) { 22291 unset($opentagpos); 22292 } 22293 if ($dom[$key]['tag']) { 22294 if ($dom[$key]['opening']) { 22295 // get text indentation (if any) 22296 if (isset($dom[$key]['text-indent']) AND $dom[$key]['block']) { 22297 $this->textindent = $dom[$key]['text-indent']; 22298 $this->newline = true; 22299 } 22300 // table 22301 if ($dom[$key]['value'] == 'table') { 22302 // available page width 22303 if ($this->rtl) { 22304 $wtmp = $this->x - $this->lMargin; 22305 } else { 22306 $wtmp = $this->w - $this->rMargin - $this->x; 22307 } 22308 // get cell spacing 22309 if (isset($dom[$key]['attribute']['cellspacing'])) { 22310 $clsp = $this->getHTMLUnitToUnits($dom[$key]['attribute']['cellspacing'], 1, 'px'); 22311 $cellspacing = array('H' => $clsp, 'V' => $clsp); 22312 } elseif (isset($dom[$key]['border-spacing'])) { 22313 $cellspacing = $dom[$key]['border-spacing']; 22314 } else { 22315 $cellspacing = array('H' => 0, 'V' => 0); 22316 } 22317 // table width 22318 if (isset($dom[$key]['width'])) { 22319 $table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px'); 22320 } else { 22321 $table_width = $wtmp; 22322 } 22323 $table_width -= (2 * $cellspacing['H']); 22324 if (!$this->inthead) { 22325 $this->y += $cellspacing['V']; 22326 } 22327 if ($this->rtl) { 22328 $cellspacingx = -$cellspacing['H']; 22329 } else { 22330 $cellspacingx = $cellspacing['H']; 22331 } 22332 // total table width without cellspaces 22333 $table_columns_width = ($table_width - ($cellspacing['H'] * ($dom[$key]['cols'] - 1))); 22334 // minimum column width 22335 $table_min_column_width = ($table_columns_width / $dom[$key]['cols']); 22336 // array of custom column widths 22337 $table_colwidths = array_fill(0, $dom[$key]['cols'], $table_min_column_width); 22338 } 22339 // table row 22340 if ($dom[$key]['value'] == 'tr') { 22341 // reset column counter 22342 $colid = 0; 22343 } 22344 // table cell 22345 if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) { 22346 $trid = $dom[$key]['parent']; 22347 $table_el = $dom[$trid]['parent']; 22348 if (!isset($dom[$table_el]['cols'])) { 22349 $dom[$table_el]['cols'] = $dom[$trid]['cols']; 22350 } 22351 // store border info 22352 $tdborder = 0; 22353 if (isset($dom[$key]['border']) AND !empty($dom[$key]['border'])) { 22354 $tdborder = $dom[$key]['border']; 22355 } 22356 $colspan = $dom[$key]['attribute']['colspan']; 22357 $old_cell_padding = $this->cell_padding; 22358 if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) { 22359 $crclpd = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px'); 22360 $current_cell_padding = array('L' => $crclpd, 'T' => $crclpd, 'R' => $crclpd, 'B' => $crclpd); 22361 } elseif (isset($dom[($dom[$trid]['parent'])]['padding'])) { 22362 $current_cell_padding = $dom[($dom[$trid]['parent'])]['padding']; 22363 } else { 22364 $current_cell_padding = array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0); 22365 } 22366 $this->cell_padding = $current_cell_padding; 22367 if (isset($dom[$key]['height'])) { 22368 // minimum cell height 22369 $cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px'); 22370 } else { 22371 $cellh = 0; 22372 } 22373 if (isset($dom[$key]['content'])) { 22374 $cell_content = stripslashes($dom[$key]['content']); 22375 } else { 22376 $cell_content = ' '; 22377 } 22378 $tagtype = $dom[$key]['value']; 22379 $parentid = $key; 22380 while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) { 22381 // move $key index forward 22382 ++$key; 22383 } 22384 if (!isset($dom[$trid]['startpage'])) { 22385 $dom[$trid]['startpage'] = $this->page; 22386 } else { 22387 $this->setPage($dom[$trid]['startpage']); 22388 } 22389 if (!isset($dom[$trid]['startcolumn'])) { 22390 $dom[$trid]['startcolumn'] = $this->current_column; 22391 } elseif ($this->current_column != $dom[$trid]['startcolumn']) { 22392 $tmpx = $this->x; 22393 $this->selectColumn($dom[$trid]['startcolumn']); 22394 $this->x = $tmpx; 22395 } 22396 if (!isset($dom[$trid]['starty'])) { 22397 $dom[$trid]['starty'] = $this->y; 22398 } else { 22399 $this->y = $dom[$trid]['starty']; 22400 } 22401 if (!isset($dom[$trid]['startx'])) { 22402 $dom[$trid]['startx'] = $this->x; 22403 $this->x += $cellspacingx; 22404 } else { 22405 $this->x += ($cellspacingx / 2); 22406 } 22407 if (isset($dom[$parentid]['attribute']['rowspan'])) { 22408 $rowspan = intval($dom[$parentid]['attribute']['rowspan']); 22409 } else { 22410 $rowspan = 1; 22411 } 22412 // skip row-spanned cells started on the previous rows 22413 if (isset($dom[$table_el]['rowspans'])) { 22414 $rsk = 0; 22415 $rskmax = count($dom[$table_el]['rowspans']); 22416 while ($rsk < $rskmax) { 22417 $trwsp = $dom[$table_el]['rowspans'][$rsk]; 22418 $rsstartx = $trwsp['startx']; 22419 $rsendx = $trwsp['endx']; 22420 // account for margin changes 22421 if ($trwsp['startpage'] < $this->page) { 22422 if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$trwsp['startpage']]['orm'])) { 22423 $dl = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$trwsp['startpage']]['orm']); 22424 $rsstartx -= $dl; 22425 $rsendx -= $dl; 22426 } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$trwsp['startpage']]['olm'])) { 22427 $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$trwsp['startpage']]['olm']); 22428 $rsstartx += $dl; 22429 $rsendx += $dl; 22430 } 22431 } 22432 if (($trwsp['rowspan'] > 0) 22433 AND ($rsstartx > ($this->x - $cellspacing['H'] - $current_cell_padding['L'] - $this->feps)) 22434 AND ($rsstartx < ($this->x + $cellspacing['H'] + $current_cell_padding['R'] + $this->feps)) 22435 AND (($trwsp['starty'] < ($this->y - $this->feps)) OR ($trwsp['startpage'] < $this->page) OR ($trwsp['startcolumn'] < $this->current_column))) { 22436 // set the starting X position of the current cell 22437 $this->x = $rsendx + $cellspacingx; 22438 // increment column indicator 22439 $colid += $trwsp['colspan']; 22440 if (($trwsp['rowspan'] == 1) 22441 AND (isset($dom[$trid]['endy'])) 22442 AND (isset($dom[$trid]['endpage'])) 22443 AND (isset($dom[$trid]['endcolumn'])) 22444 AND ($trwsp['endpage'] == $dom[$trid]['endpage']) 22445 AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) { 22446 // set ending Y position for row 22447 $dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']); 22448 $dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy']; 22449 } 22450 $rsk = 0; 22451 } else { 22452 ++$rsk; 22453 } 22454 } 22455 } 22456 if (isset($dom[$parentid]['width'])) { 22457 // user specified width 22458 $cellw = $this->getHTMLUnitToUnits($dom[$parentid]['width'], $table_columns_width, 'px'); 22459 $tmpcw = ($cellw / $colspan); 22460 for ($i = 0; $i < $colspan; ++$i) { 22461 $table_colwidths[($colid + $i)] = $tmpcw; 22462 } 22463 } else { 22464 // inherit column width 22465 $cellw = 0; 22466 for ($i = 0; $i < $colspan; ++$i) { 22467 $cellw += $table_colwidths[($colid + $i)]; 22468 } 22469 } 22470 $cellw += (($colspan - 1) * $cellspacing['H']); 22471 // increment column indicator 22472 $colid += $colspan; 22473 // add rowspan information to table element 22474 if ($rowspan > 1) { 22475 $trsid = array_push($dom[$table_el]['rowspans'], array('trid' => $trid, 'rowspan' => $rowspan, 'mrowspan' => $rowspan, 'colspan' => $colspan, 'startpage' => $this->page, 'startcolumn' => $this->current_column, 'startx' => $this->x, 'starty' => $this->y)); 22476 } 22477 $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x)); 22478 if ($rowspan > 1) { 22479 $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1); 22480 } 22481 // push background colors 22482 if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) { 22483 $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor']; 22484 } 22485 // store border info 22486 if (isset($tdborder) AND !empty($tdborder)) { 22487 $dom[$trid]['cellpos'][($cellid - 1)]['border'] = $tdborder; 22488 } 22489 $prevLastH = $this->lasth; 22490 // store some info for multicolumn mode 22491 if ($this->rtl) { 22492 $this->colxshift['x'] = $this->w - $this->x - $this->rMargin; 22493 } else { 22494 $this->colxshift['x'] = $this->x - $this->lMargin; 22495 } 22496 $this->colxshift['s'] = $cellspacing; 22497 $this->colxshift['p'] = $current_cell_padding; 22498 // ****** write the cell content ****** 22499 $this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true, true, 0, 'T', false); 22500 // restore some values 22501 $this->colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0)); 22502 $this->lasth = $prevLastH; 22503 $this->cell_padding = $old_cell_padding; 22504 $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x; 22505 // update the end of row position 22506 if ($rowspan <= 1) { 22507 if (isset($dom[$trid]['endy'])) { 22508 if (($this->page == $dom[$trid]['endpage']) AND ($this->current_column == $dom[$trid]['endcolumn'])) { 22509 $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']); 22510 } elseif (($this->page > $dom[$trid]['endpage']) OR ($this->current_column > $dom[$trid]['endcolumn'])) { 22511 $dom[$trid]['endy'] = $this->y; 22512 } 22513 } else { 22514 $dom[$trid]['endy'] = $this->y; 22515 } 22516 if (isset($dom[$trid]['endpage'])) { 22517 $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']); 22518 } else { 22519 $dom[$trid]['endpage'] = $this->page; 22520 } 22521 if (isset($dom[$trid]['endcolumn'])) { 22522 $dom[$trid]['endcolumn'] = max($this->current_column, $dom[$trid]['endcolumn']); 22523 } else { 22524 $dom[$trid]['endcolumn'] = $this->current_column; 22525 } 22526 } else { 22527 // account for row-spanned cells 22528 $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x; 22529 $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y; 22530 $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page; 22531 $dom[$table_el]['rowspans'][($trsid - 1)]['endcolumn'] = $this->current_column; 22532 } 22533 if (isset($dom[$table_el]['rowspans'])) { 22534 // update endy and endpage on rowspanned cells 22535 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) { 22536 if ($trwsp['rowspan'] > 0) { 22537 if (isset($dom[$trid]['endpage'])) { 22538 if (($trwsp['endpage'] == $dom[$trid]['endpage']) AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) { 22539 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']); 22540 } elseif (($trwsp['endpage'] < $dom[$trid]['endpage']) OR ($trwsp['endcolumn'] < $dom[$trid]['endcolumn'])) { 22541 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy']; 22542 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage']; 22543 $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[$trid]['endcolumn']; 22544 } else { 22545 $dom[$trid]['endy'] = $this->pagedim[$dom[$trid]['endpage']]['hk'] - $this->pagedim[$dom[$trid]['endpage']]['bm']; 22546 } 22547 } 22548 } 22549 } 22550 } 22551 $this->x += ($cellspacingx / 2); 22552 } else { 22553 // opening tag (or self-closing tag) 22554 if (!isset($opentagpos)) { 22555 if ($this->inxobj) { 22556 // we are inside an XObject template 22557 $opentagpos = strlen($this->xobjects[$this->xobjid]['outdata']); 22558 } elseif (!$this->InFooter) { 22559 if (isset($this->footerlen[$this->page])) { 22560 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page]; 22561 } else { 22562 $this->footerpos[$this->page] = $this->pagelen[$this->page]; 22563 } 22564 $opentagpos = $this->footerpos[$this->page]; 22565 } 22566 } 22567 $dom = $this->openHTMLTagHandler($dom, $key, $cell); 22568 } 22569 } else { // closing tag 22570 $prev_numpages = $this->numpages; 22571 $old_bordermrk = $this->bordermrk[$this->page]; 22572 $dom = $this->closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney); 22573 if ($this->bordermrk[$this->page] > $old_bordermrk) { 22574 $startlinepos += ($this->bordermrk[$this->page] - $old_bordermrk); 22575 } 22576 if ($prev_numpages > $this->numpages) { 22577 $startlinepage = $this->page; 22578 } 22579 } 22580 } elseif (strlen($dom[$key]['value']) > 0) { 22581 // print list-item 22582 if (!$this->empty_string($this->lispacer) AND ($this->lispacer != '^')) { 22583 $this->SetFont($pfontname, $pfontstyle, $pfontsize); 22584 $this->resetLastH(); 22585 $minstartliney = $this->y; 22586 $maxbottomliney = ($startliney + ($this->FontSize * $this->cell_height_ratio)); 22587 $this->putHtmlListBullet($this->listnum, $this->lispacer, $pfontsize); 22588 $this->SetFont($curfontname, $curfontstyle, $curfontsize); 22589 $this->resetLastH(); 22590 if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) { 22591 $pfontascent = $this->getFontAscent($pfontname, $pfontstyle, $pfontsize); 22592 $pfontdescent = $this->getFontDescent($pfontname, $pfontstyle, $pfontsize); 22593 $this->y += ((($pfontsize - $curfontsize) * $this->cell_height_ratio / $this->k) + $pfontascent - $curfontascent - $pfontdescent + $curfontdescent) / 2; 22594 $minstartliney = min($this->y, $minstartliney); 22595 $maxbottomliney = max(($this->y + (($pfontsize * $this->cell_height_ratio) / $this->k)), $maxbottomliney); 22596 } 22597 } 22598 // text 22599 $this->htmlvspace = 0; 22600 if ((!$this->premode) AND $this->isRTLTextDir()) { 22601 // reverse spaces order 22602 $lsp = ''; // left spaces 22603 $rsp = ''; // right spaces 22604 if (preg_match('/^('.$this->re_space['p'].'+)/'.$this->re_space['m'], $dom[$key]['value'], $matches)) { 22605 $lsp = $matches[1]; 22606 } 22607 if (preg_match('/('.$this->re_space['p'].'+)$/'.$this->re_space['m'], $dom[$key]['value'], $matches)) { 22608 $rsp = $matches[1]; 22609 } 22610 $dom[$key]['value'] = $rsp.$this->stringTrim($dom[$key]['value']).$lsp; 22611 } 22612 if ($newline) { 22613 if (!$this->premode) { 22614 $prelen = strlen($dom[$key]['value']); 22615 if ($this->isRTLTextDir()) { 22616 // right trim except non-breaking space 22617 $dom[$key]['value'] = $this->stringRightTrim($dom[$key]['value']); 22618 } else { 22619 // left trim except non-breaking space 22620 $dom[$key]['value'] = $this->stringLeftTrim($dom[$key]['value']); 22621 } 22622 $postlen = strlen($dom[$key]['value']); 22623 if (($postlen == 0) AND ($prelen > 0)) { 22624 $dom[$key]['trimmed_space'] = true; 22625 } 22626 } 22627 $newline = false; 22628 $firstblock = true; 22629 } else { 22630 $firstblock = false; 22631 // replace empty multiple spaces string with a single space 22632 $dom[$key]['value'] = preg_replace('/^'.$this->re_space['p'].'+$/'.$this->re_space['m'], chr(32), $dom[$key]['value']); 22633 } 22634 $strrest = ''; 22635 if ($this->rtl) { 22636 $this->x -= $this->textindent; 22637 } else { 22638 $this->x += $this->textindent; 22639 } 22640 if (!isset($dom[$key]['trimmed_space']) OR !$dom[$key]['trimmed_space']) { 22641 $strlinelen = $this->GetStringWidth($dom[$key]['value']); 22642 if (!empty($this->HREF) AND (isset($this->HREF['url']))) { 22643 // HTML <a> Link 22644 $hrefcolor = ''; 22645 if (isset($dom[($dom[$key]['parent'])]['fgcolor']) AND ($dom[($dom[$key]['parent'])]['fgcolor'] !== false)) { 22646 $hrefcolor = $dom[($dom[$key]['parent'])]['fgcolor']; 22647 } 22648 $hrefstyle = -1; 22649 if (isset($dom[($dom[$key]['parent'])]['fontstyle']) AND ($dom[($dom[$key]['parent'])]['fontstyle'] !== false)) { 22650 $hrefstyle = $dom[($dom[$key]['parent'])]['fontstyle']; 22651 } 22652 $strrest = $this->addHtmlLink($this->HREF['url'], $dom[$key]['value'], $wfill, true, $hrefcolor, $hrefstyle, true); 22653 } else { 22654 $wadj = 0; // space to leave for block continuity 22655 if ($this->rtl) { 22656 $cwa = $this->x - $this->lMargin; 22657 } else { 22658 $cwa = $this->w - $this->rMargin - $this->x; 22659 } 22660 if (($strlinelen < $cwa) AND (isset($dom[($key + 1)])) AND ($dom[($key + 1)]['tag']) AND (!$dom[($key + 1)]['block'])) { 22661 // check the next text blocks for continuity 22662 $nkey = ($key + 1); 22663 $write_block = true; 22664 $same_textdir = true; 22665 $tmp_fontname = $this->FontFamily; 22666 $tmp_fontstyle = $this->FontStyle; 22667 $tmp_fontsize = $this->FontSizePt; 22668 while ($write_block AND isset($dom[$nkey])) { 22669 if ($dom[$nkey]['tag']) { 22670 if ($dom[$nkey]['block']) { 22671 // end of block 22672 $write_block = false; 22673 } 22674 $tmp_fontname = isset($dom[$nkey]['fontname']) ? $dom[$nkey]['fontname'] : $this->FontFamily; 22675 $tmp_fontstyle = isset($dom[$nkey]['fontstyle']) ? $dom[$nkey]['fontstyle'] : $this->FontStyle; 22676 $tmp_fontsize = isset($dom[$nkey]['fontsize']) ? $dom[$nkey]['fontsize'] : $this->FontSizePt; 22677 $same_textdir = ($dom[$nkey]['dir'] == $dom[$key]['dir']); 22678 } else { 22679 $nextstr = preg_split('/'.$this->re_space['p'].'+/'.$this->re_space['m'], $dom[$nkey]['value']); 22680 if (isset($nextstr[0]) AND $same_textdir) { 22681 $wadj += $this->GetStringWidth($nextstr[0], $tmp_fontname, $tmp_fontstyle, $tmp_fontsize); 22682 } 22683 if (isset($nextstr[1])) { 22684 $write_block = false; 22685 } 22686 } 22687 ++$nkey; 22688 } 22689 } 22690 if (($wadj > 0) AND (($strlinelen + $wadj) >= $cwa)) { 22691 $wadj = 0; 22692 $nextstr = preg_split('/'.$this->re_space['p'].'/'.$this->re_space['m'], $dom[$key]['value']); 22693 $numblks = count($nextstr); 22694 if ($numblks > 1) { 22695 // try to split on blank spaces 22696 $wadj = ($cwa - $strlinelen + $this->GetStringWidth($nextstr[($numblks - 1)])); 22697 } 22698 } 22699 // check for reversed text direction 22700 if (($wadj > 0) AND (($this->rtl AND ($this->tmprtl === 'L')) OR (!$this->rtl AND ($this->tmprtl === 'R')))) { 22701 // LTR text on RTL direction or RTL text on LTR direction 22702 $reverse_dir = true; 22703 $this->rtl = !$this->rtl; 22704 $revshift = ($strlinelen + $wadj + 0.000001); // add little quantity for rounding problems 22705 if ($this->rtl) { 22706 $this->x += $revshift; 22707 } else { 22708 $this->x -= $revshift; 22709 } 22710 $xws = $this->x; 22711 } 22712 // ****** write only until the end of the line and get the rest ****** 22713 $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock, 0, $wadj); 22714 // restore default direction 22715 if ($reverse_dir AND ($wadj == 0)) { 22716 $this->x = $xws; 22717 $this->rtl = !$this->rtl; 22718 $reverse_dir = false; 22719 } 22720 } 22721 } 22722 $this->textindent = 0; 22723 if (strlen($strrest) > 0) { 22724 // store the remaining string on the previous $key position 22725 $this->newline = true; 22726 if ($strrest == $dom[$key]['value']) { 22727 // used to avoid infinite loop 22728 ++$loop; 22729 } else { 22730 $loop = 0; 22731 } 22732 $dom[$key]['value'] = $strrest; 22733 if ($cell) { 22734 if ($this->rtl) { 22735 $this->x -= $this->cell_padding['R']; 22736 } else { 22737 $this->x += $this->cell_padding['L']; 22738 } 22739 } 22740 if ($loop < 3) { 22741 --$key; 22742 } 22743 } else { 22744 $loop = 0; 22745 } 22746 } 22747 ++$key; 22748 if (isset($dom[$key]['tag']) AND $dom[$key]['tag'] AND (!isset($dom[$key]['opening']) OR !$dom[$key]['opening']) AND isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) { 22749 // check if we are on a new page or on a new column 22750 if ((!$undo) AND (($this->y < $this->start_transaction_y) OR (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['endy'] < $this->start_transaction_y)))) { 22751 // we are on a new page or on a new column and the total object height is less than the available vertical space. 22752 // restore previous object 22753 $this->rollbackTransaction(true); 22754 // restore previous values 22755 foreach ($this_method_vars as $vkey => $vval) { 22756 $$vkey = $vval; 22757 } 22758 // add a page (or trig AcceptPageBreak() for multicolumn mode) 22759 $pre_y = $this->y; 22760 if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) { 22761 $startliney = $this->y; 22762 } 22763 $undo = true; // avoid infinite loop 22764 } else { 22765 $undo = false; 22766 } 22767 } 22768 } // end for each $key 22769 // align the last line 22770 if (isset($startlinex)) { 22771 $yshift = ($minstartliney - $startliney); 22772 if (($yshift > 0) OR ($this->page > $startlinepage)) { 22773 $yshift = 0; 22774 } 22775 $t_x = 0; 22776 // the last line must be shifted to be aligned as requested 22777 $linew = abs($this->endlinex - $startlinex); 22778 if ($this->inxobj) { 22779 // we are inside an XObject template 22780 $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos); 22781 if (isset($opentagpos)) { 22782 $midpos = $opentagpos; 22783 } else { 22784 $midpos = 0; 22785 } 22786 if ($midpos > 0) { 22787 $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos)); 22788 $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos); 22789 } else { 22790 $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos); 22791 $pend = ''; 22792 } 22793 } else { 22794 $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos); 22795 if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) { 22796 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 22797 $midpos = min($opentagpos, $this->footerpos[$startlinepage]); 22798 } elseif (isset($opentagpos)) { 22799 $midpos = $opentagpos; 22800 } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) { 22801 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 22802 $midpos = $this->footerpos[$startlinepage]; 22803 } else { 22804 $midpos = 0; 22805 } 22806 if ($midpos > 0) { 22807 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos)); 22808 $pend = substr($this->getPageBuffer($startlinepage), $midpos); 22809 } else { 22810 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos); 22811 $pend = ''; 22812 } 22813 } 22814 if ((isset($plalign) AND ((($plalign == 'C') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) { 22815 // calculate shifting amount 22816 $tw = $w; 22817 if ($this->lMargin != $prevlMargin) { 22818 $tw += ($prevlMargin - $this->lMargin); 22819 } 22820 if ($this->rMargin != $prevrMargin) { 22821 $tw += ($prevrMargin - $this->rMargin); 22822 } 22823 $one_space_width = $this->GetStringWidth(chr(32)); 22824 $no = 0; // number of spaces on a line contained on a single block 22825 if ($this->isRTLTextDir()) { // RTL 22826 // remove left space if exist 22827 $pos1 = $this->revstrpos($pmid, '[('); 22828 if ($pos1 > 0) { 22829 $pos1 = intval($pos1); 22830 if ($this->isUnicodeFont()) { 22831 $pos2 = intval($this->revstrpos($pmid, '[('.chr(0).chr(32))); 22832 $spacelen = 2; 22833 } else { 22834 $pos2 = intval($this->revstrpos($pmid, '[('.chr(32))); 22835 $spacelen = 1; 22836 } 22837 if ($pos1 == $pos2) { 22838 $pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen)); 22839 if (substr($pmid, $pos1, 4) == '[()]') { 22840 $linew -= $one_space_width; 22841 } elseif ($pos1 == strpos($pmid, '[(')) { 22842 $no = 1; 22843 } 22844 } 22845 } 22846 } else { // LTR 22847 // remove right space if exist 22848 $pos1 = $this->revstrpos($pmid, ')]'); 22849 if ($pos1 > 0) { 22850 $pos1 = intval($pos1); 22851 if ($this->isUnicodeFont()) { 22852 $pos2 = intval($this->revstrpos($pmid, chr(0).chr(32).')]')) + 2; 22853 $spacelen = 2; 22854 } else { 22855 $pos2 = intval($this->revstrpos($pmid, chr(32).')]')) + 1; 22856 $spacelen = 1; 22857 } 22858 if ($pos1 == $pos2) { 22859 $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1); 22860 $linew -= $one_space_width; 22861 } 22862 } 22863 } 22864 $mdiff = ($tw - $linew); 22865 if ($plalign == 'C') { 22866 if ($this->rtl) { 22867 $t_x = -($mdiff / 2); 22868 } else { 22869 $t_x = ($mdiff / 2); 22870 } 22871 } elseif ($plalign == 'R') { 22872 // right alignment on LTR document 22873 $t_x = $mdiff; 22874 } elseif ($plalign == 'L') { 22875 // left alignment on RTL document 22876 $t_x = -$mdiff; 22877 } 22878 } // end if startlinex 22879 if (($t_x != 0) OR ($yshift < 0)) { 22880 // shift the line 22881 $trx = sprintf('1 0 0 1 %.3F %.3F cm', ($t_x * $this->k), ($yshift * $this->k)); 22882 $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n"; 22883 $endlinepos = strlen($pstart); 22884 if ($this->inxobj) { 22885 // we are inside an XObject template 22886 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend; 22887 foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) { 22888 if ($pak >= $pask) { 22889 $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x; 22890 $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift; 22891 } 22892 } 22893 } else { 22894 $this->setPageBuffer($startlinepage, $pstart.$pend); 22895 // shift the annotations and links 22896 if (isset($this->PageAnnots[$this->page])) { 22897 foreach ($this->PageAnnots[$this->page] as $pak => $pac) { 22898 if ($pak >= $pask) { 22899 $this->PageAnnots[$this->page][$pak]['x'] += $t_x; 22900 $this->PageAnnots[$this->page][$pak]['y'] -= $yshift; 22901 } 22902 } 22903 } 22904 } 22905 $this->y -= $yshift; 22906 $yshift = 0; 22907 } 22908 } 22909 // restore previous values 22910 $this->setGraphicVars($gvars); 22911 if ($this->num_columns > 1) { 22912 $this->selectColumn(); 22913 } elseif ($this->page > $prevPage) { 22914 $this->lMargin = $this->pagedim[$this->page]['olm']; 22915 $this->rMargin = $this->pagedim[$this->page]['orm']; 22916 } 22917 // restore previous list state 22918 $this->cell_height_ratio = $prev_cell_height_ratio; 22919 $this->listnum = $prev_listnum; 22920 $this->listordered = $prev_listordered; 22921 $this->listcount = $prev_listcount; 22922 $this->lispacer = $prev_lispacer; 22923 if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) { 22924 $this->Ln($this->lasth); 22925 if ($this->y < $maxbottomliney) { 22926 $this->y = $maxbottomliney; 22927 } 22928 } 22929 unset($dom); 22930 } 22931 22940 protected function openHTMLTagHandler($dom, $key, $cell) { 22941 $tag = $dom[$key]; 22942 $parent = $dom[($dom[$key]['parent'])]; 22943 $firsttag = ($key == 1); 22944 // check for text direction attribute 22945 if (isset($tag['dir'])) { 22946 $this->setTempRTL($tag['dir']); 22947 } else { 22948 $this->tmprtl = false; 22949 } 22950 if ($tag['block']) { 22951 $hbz = 0; // distance from y to line bottom 22952 $hb = 0; // vertical space between block tags 22953 // calculate vertical space for block tags 22954 if (isset($this->tagvspaces[$tag['value']][0]['h']) AND ($this->tagvspaces[$tag['value']][0]['h'] >= 0)) { 22955 $cur_h = $this->tagvspaces[$tag['value']][0]['h']; 22956 } elseif (isset($tag['fontsize'])) { 22957 $cur_h = ($tag['fontsize'] / $this->k) * $this->cell_height_ratio; 22958 } else { 22959 $cur_h = $this->FontSize * $this->cell_height_ratio; 22960 } 22961 if (isset($this->tagvspaces[$tag['value']][0]['n'])) { 22962 $n = $this->tagvspaces[$tag['value']][0]['n']; 22963 } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) { 22964 $n = 0.6; 22965 } else { 22966 $n = 1; 22967 } 22968 if ((!isset($this->tagvspaces[$tag['value']])) AND (in_array($tag['value'], array('div', 'dt', 'dd', 'li', 'br')))) { 22969 $hb = 0; 22970 } else { 22971 $hb = ($n * $cur_h); 22972 } 22973 if (($this->htmlvspace <= 0) AND ($n > 0)) { 22974 if (isset($parent['fontsize'])) { 22975 $hbz = (($parent['fontsize'] / $this->k) * $this->cell_height_ratio); 22976 } else { 22977 $hbz = $this->FontSize * $this->cell_height_ratio; 22978 } 22979 } 22980 } 22981 // Opening tag 22982 switch($tag['value']) { 22983 case 'table': { 22984 $cp = 0; 22985 $cs = 0; 22986 $dom[$key]['rowspans'] = array(); 22987 if (!isset($dom[$key]['attribute']['nested']) OR ($dom[$key]['attribute']['nested'] != 'true')) { 22988 // set table header 22989 if (!$this->empty_string($dom[$key]['thead'])) { 22990 // set table header 22991 $this->thead = $dom[$key]['thead']; 22992 if (!isset($this->theadMargins) OR (empty($this->theadMargins))) { 22993 $this->theadMargins = array(); 22994 $this->theadMargins['cell_padding'] = $this->cell_padding; 22995 $this->theadMargins['lmargin'] = $this->lMargin; 22996 $this->theadMargins['rmargin'] = $this->rMargin; 22997 $this->theadMargins['page'] = $this->page; 22998 $this->theadMargins['cell'] = $cell; 22999 } 23000 } 23001 } 23002 // store current margins and page 23003 $dom[$key]['old_cell_padding'] = $this->cell_padding; 23004 if (isset($tag['attribute']['cellpadding'])) { 23005 $pad = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px'); 23006 $this->SetCellPadding($pad); 23007 } elseif (isset($tag['padding'])) { 23008 $this->cell_padding = $tag['padding']; 23009 } 23010 if (isset($tag['attribute']['cellspacing'])) { 23011 $cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px'); 23012 } elseif (isset($tag['border-spacing'])) { 23013 $cs = $tag['border-spacing']['V']; 23014 } 23015 $prev_y = $this->y; 23016 if ($this->checkPageBreak(((2 * $cp) + (2 * $cs) + $this->lasth), '', false) OR ($this->y < $prev_y)) { 23017 $this->inthead = true; 23018 // add a page (or trig AcceptPageBreak() for multicolumn mode) 23019 $this->checkPageBreak($this->PageBreakTrigger + 1); 23020 } 23021 break; 23022 } 23023 case 'tr': { 23024 // array of columns positions 23025 $dom[$key]['cellpos'] = array(); 23026 break; 23027 } 23028 case 'hr': { 23029 if ((isset($tag['height'])) AND ($tag['height'] != '')) { 23030 $hrHeight = $this->getHTMLUnitToUnits($tag['height'], 1, 'px'); 23031 } else { 23032 $hrHeight = $this->GetLineWidth(); 23033 } 23034 $this->addHTMLVertSpace($hbz, ($hrHeight / 2), $cell, $firsttag); 23035 $x = $this->GetX(); 23036 $y = $this->GetY(); 23037 $wtmp = $this->w - $this->lMargin - $this->rMargin; 23038 if ($cell) { 23039 $wtmp -= ($this->cell_padding['L'] + $this->cell_padding['R']); 23040 } 23041 if ((isset($tag['width'])) AND ($tag['width'] != '')) { 23042 $hrWidth = $this->getHTMLUnitToUnits($tag['width'], $wtmp, 'px'); 23043 } else { 23044 $hrWidth = $wtmp; 23045 } 23046 $prevlinewidth = $this->GetLineWidth(); 23047 $this->SetLineWidth($hrHeight); 23048 $this->Line($x, $y, $x + $hrWidth, $y); 23049 $this->SetLineWidth($prevlinewidth); 23050 $this->addHTMLVertSpace(($hrHeight / 2), 0, $cell, !isset($dom[($key + 1)])); 23051 break; 23052 } 23053 case 'a': { 23054 if (array_key_exists('href', $tag['attribute'])) { 23055 $this->HREF['url'] = $tag['attribute']['href']; 23056 } 23057 break; 23058 } 23059 case 'img': { 23060 if (isset($tag['attribute']['src'])) { 23061 if ($tag['attribute']['src']{0} === '@') { 23062 // data stream 23063 $tag['attribute']['src'] = '@'.base64_decode(substr($tag['attribute']['src'], 1)); 23064 $type = ''; 23065 } else { 23066 // check for images without protocol 23067 if (preg_match('%^/{2}%', $tag['attribute']['src'])) { 23068 $tag['attribute']['src'] = 'http:'.$tag['attribute']['src']; 23069 } 23070 // replace relative path with real server path 23071 if (($tag['attribute']['src'][0] == '/') AND !empty($_SERVER['DOCUMENT_ROOT']) AND ($_SERVER['DOCUMENT_ROOT'] != '/')) { 23072 $findroot = strpos($tag['attribute']['src'], $_SERVER['DOCUMENT_ROOT']); 23073 if (($findroot === false) OR ($findroot > 1)) { 23074 if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') { 23075 $tag['attribute']['src'] = substr($_SERVER['DOCUMENT_ROOT'], 0, -1).$tag['attribute']['src']; 23076 } else { 23077 $tag['attribute']['src'] = $_SERVER['DOCUMENT_ROOT'].$tag['attribute']['src']; 23078 } 23079 } 23080 } 23081 $tag['attribute']['src'] = htmlspecialchars_decode(urldecode($tag['attribute']['src'])); 23082 $type = $this->getImageFileType($tag['attribute']['src']); 23083 $testscrtype = @parse_url($tag['attribute']['src']); 23084 if (!isset($testscrtype['query']) OR empty($testscrtype['query'])) { 23085 // convert URL to server path 23086 $tag['attribute']['src'] = str_replace(K_PATH_URL, K_PATH_MAIN, $tag['attribute']['src']); 23087 } 23088 } 23089 if (!isset($tag['width'])) { 23090 $tag['width'] = 0; 23091 } 23092 if (!isset($tag['height'])) { 23093 $tag['height'] = 0; 23094 } 23095 //if (!isset($tag['attribute']['align'])) { 23096 // the only alignment supported is "bottom" 23097 // further development is required for other modes. 23098 $tag['attribute']['align'] = 'bottom'; 23099 //} 23100 switch($tag['attribute']['align']) { 23101 case 'top': { 23102 $align = 'T'; 23103 break; 23104 } 23105 case 'middle': { 23106 $align = 'M'; 23107 break; 23108 } 23109 case 'bottom': { 23110 $align = 'B'; 23111 break; 23112 } 23113 default: { 23114 $align = 'B'; 23115 break; 23116 } 23117 } 23118 $prevy = $this->y; 23119 $xpos = $this->x; 23120 $imglink = ''; 23121 if (isset($this->HREF['url']) AND !$this->empty_string($this->HREF['url'])) { 23122 $imglink = $this->HREF['url']; 23123 if ($imglink{0} == '#') { 23124 // convert url to internal link 23125 $lnkdata = explode(',', $imglink); 23126 if (isset($lnkdata[0])) { 23127 $page = intval(substr($lnkdata[0], 1)); 23128 if (empty($page) OR ($page <= 0)) { 23129 $page = $this->page; 23130 } 23131 if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) { 23132 $lnky = floatval($lnkdata[1]); 23133 } else { 23134 $lnky = 0; 23135 } 23136 $imglink = $this->AddLink(); 23137 $this->SetLink($imglink, $lnky, $page); 23138 } 23139 } 23140 } 23141 $border = 0; 23142 if (isset($tag['border']) AND !empty($tag['border'])) { 23143 // currently only support 1 (frame) or a combination of 'LTRB' 23144 $border = $tag['border']; 23145 } 23146 $iw = ''; 23147 if (isset($tag['width'])) { 23148 $iw = $this->getHTMLUnitToUnits($tag['width'], 1, 'px', false); 23149 } 23150 $ih = ''; 23151 if (isset($tag['height'])) { 23152 $ih = $this->getHTMLUnitToUnits($tag['height'], 1, 'px', false); 23153 } 23154 if (($type == 'eps') OR ($type == 'ai')) { 23155 $this->ImageEps($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, true, $align, '', $border, true); 23156 } elseif ($type == 'svg') { 23157 $this->ImageSVG($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, $align, '', $border, true); 23158 } else { 23159 $this->Image($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border, false, false, true); 23160 } 23161 switch($align) { 23162 case 'T': { 23163 $this->y = $prevy; 23164 break; 23165 } 23166 case 'M': { 23167 $this->y = (($this->img_rb_y + $prevy - ($tag['fontsize'] / $this->k)) / 2) ; 23168 break; 23169 } 23170 case 'B': { 23171 $this->y = $this->img_rb_y - ($tag['fontsize'] / $this->k); 23172 break; 23173 } 23174 } 23175 } 23176 break; 23177 } 23178 case 'dl': { 23179 ++$this->listnum; 23180 if ($this->listnum == 1) { 23181 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 23182 } else { 23183 $this->addHTMLVertSpace(0, 0, $cell, $firsttag); 23184 } 23185 break; 23186 } 23187 case 'dt': { 23188 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 23189 break; 23190 } 23191 case 'dd': { 23192 if ($this->rtl) { 23193 $this->rMargin += $this->listindent; 23194 } else { 23195 $this->lMargin += $this->listindent; 23196 } 23197 ++$this->listindentlevel; 23198 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 23199 break; 23200 } 23201 case 'ul': 23202 case 'ol': { 23203 ++$this->listnum; 23204 if ($tag['value'] == 'ol') { 23205 $this->listordered[$this->listnum] = true; 23206 } else { 23207 $this->listordered[$this->listnum] = false; 23208 } 23209 if (isset($tag['attribute']['start'])) { 23210 $this->listcount[$this->listnum] = intval($tag['attribute']['start']) - 1; 23211 } else { 23212 $this->listcount[$this->listnum] = 0; 23213 } 23214 if ($this->rtl) { 23215 $this->rMargin += $this->listindent; 23216 $this->x -= $this->listindent; 23217 } else { 23218 $this->lMargin += $this->listindent; 23219 $this->x += $this->listindent; 23220 } 23221 ++$this->listindentlevel; 23222 if ($this->listnum == 1) { 23223 if ($key > 1) { 23224 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 23225 } 23226 } else { 23227 $this->addHTMLVertSpace(0, 0, $cell, $firsttag); 23228 } 23229 break; 23230 } 23231 case 'li': { 23232 if ($key > 2) { 23233 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 23234 } 23235 if ($this->listordered[$this->listnum]) { 23236 // ordered item 23237 if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) { 23238 $this->lispacer = $parent['attribute']['type']; 23239 } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) { 23240 $this->lispacer = $parent['listtype']; 23241 } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) { 23242 $this->lispacer = $this->lisymbol; 23243 } else { 23244 $this->lispacer = '#'; 23245 } 23246 ++$this->listcount[$this->listnum]; 23247 if (isset($tag['attribute']['value'])) { 23248 $this->listcount[$this->listnum] = intval($tag['attribute']['value']); 23249 } 23250 } else { 23251 // unordered item 23252 if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) { 23253 $this->lispacer = $parent['attribute']['type']; 23254 } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) { 23255 $this->lispacer = $parent['listtype']; 23256 } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) { 23257 $this->lispacer = $this->lisymbol; 23258 } else { 23259 $this->lispacer = '!'; 23260 } 23261 } 23262 break; 23263 } 23264 case 'blockquote': { 23265 if ($this->rtl) { 23266 $this->rMargin += $this->listindent; 23267 } else { 23268 $this->lMargin += $this->listindent; 23269 } 23270 ++$this->listindentlevel; 23271 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 23272 break; 23273 } 23274 case 'br': { 23275 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 23276 break; 23277 } 23278 case 'div': { 23279 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 23280 break; 23281 } 23282 case 'p': { 23283 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 23284 break; 23285 } 23286 case 'pre': { 23287 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 23288 $this->premode = true; 23289 break; 23290 } 23291 case 'sup': { 23292 $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k)); 23293 break; 23294 } 23295 case 'sub': { 23296 $this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k)); 23297 break; 23298 } 23299 case 'h1': 23300 case 'h2': 23301 case 'h3': 23302 case 'h4': 23303 case 'h5': 23304 case 'h6': { 23305 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); 23306 break; 23307 } 23308 // Form fields (since 4.8.000 - 2009-09-07) 23309 case 'form': { 23310 if (isset($tag['attribute']['action'])) { 23311 $this->form_action = $tag['attribute']['action']; 23312 } else { 23313 $this->form_action = K_PATH_URL.$_SERVER['SCRIPT_NAME']; 23314 } 23315 if (isset($tag['attribute']['enctype'])) { 23316 $this->form_enctype = $tag['attribute']['enctype']; 23317 } else { 23318 $this->form_enctype = 'application/x-www-form-urlencoded'; 23319 } 23320 if (isset($tag['attribute']['method'])) { 23321 $this->form_mode = $tag['attribute']['method']; 23322 } else { 23323 $this->form_mode = 'post'; 23324 } 23325 break; 23326 } 23327 case 'input': { 23328 if (isset($tag['attribute']['name']) AND !$this->empty_string($tag['attribute']['name'])) { 23329 $name = $tag['attribute']['name']; 23330 } else { 23331 break; 23332 } 23333 $prop = array(); 23334 $opt = array(); 23335 if (isset($tag['attribute']['readonly']) AND !$this->empty_string($tag['attribute']['readonly'])) { 23336 $prop['readonly'] = true; 23337 } 23338 if (isset($tag['attribute']['value']) AND !$this->empty_string($tag['attribute']['value'])) { 23339 $value = $tag['attribute']['value']; 23340 } 23341 if (isset($tag['attribute']['maxlength']) AND !$this->empty_string($tag['attribute']['maxlength'])) { 23342 $opt['maxlen'] = intval($tag['attribute']['value']); 23343 } 23344 $h = $this->FontSize * $this->cell_height_ratio; 23345 if (isset($tag['attribute']['size']) AND !$this->empty_string($tag['attribute']['size'])) { 23346 $w = intval($tag['attribute']['size']) * $this->GetStringWidth(chr(32)) * 2; 23347 } else { 23348 $w = $h; 23349 } 23350 if (isset($tag['attribute']['checked']) AND (($tag['attribute']['checked'] == 'checked') OR ($tag['attribute']['checked'] == 'true'))) { 23351 $checked = true; 23352 } else { 23353 $checked = false; 23354 } 23355 if (isset($tag['align'])) { 23356 switch ($tag['align']) { 23357 case 'C': { 23358 $opt['q'] = 1; 23359 break; 23360 } 23361 case 'R': { 23362 $opt['q'] = 2; 23363 break; 23364 } 23365 case 'L': 23366 default: { 23367 break; 23368 } 23369 } 23370 } 23371 switch ($tag['attribute']['type']) { 23372 case 'text': { 23373 if (isset($value)) { 23374 $opt['v'] = $value; 23375 } 23376 $this->TextField($name, $w, $h, $prop, $opt, '', '', false); 23377 break; 23378 } 23379 case 'password': { 23380 if (isset($value)) { 23381 $opt['v'] = $value; 23382 } 23383 $prop['password'] = 'true'; 23384 $this->TextField($name, $w, $h, $prop, $opt, '', '', false); 23385 break; 23386 } 23387 case 'checkbox': { 23388 $this->CheckBox($name, $w, $checked, $prop, $opt, $value, '', '', false); 23389 break; 23390 } 23391 case 'radio': { 23392 $this->RadioButton($name, $w, $prop, $opt, $value, $checked, '', '', false); 23393 break; 23394 } 23395 case 'submit': { 23396 $w = $this->GetStringWidth($value) * 1.5; 23397 $h *= 1.6; 23398 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255)); 23399 $action = array(); 23400 $action['S'] = 'SubmitForm'; 23401 $action['F'] = $this->form_action; 23402 if ($this->form_enctype != 'FDF') { 23403 $action['Flags'] = array('ExportFormat'); 23404 } 23405 if ($this->form_mode == 'get') { 23406 $action['Flags'] = array('GetMethod'); 23407 } 23408 $this->Button($name, $w, $h, $value, $action, $prop, $opt, '', '', false); 23409 break; 23410 } 23411 case 'reset': { 23412 $w = $this->GetStringWidth($value) * 1.5; 23413 $h *= 1.6; 23414 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255)); 23415 $this->Button($name, $w, $h, $value, array('S'=>'ResetForm'), $prop, $opt, '', '', false); 23416 break; 23417 } 23418 case 'file': { 23419 $prop['fileSelect'] = 'true'; 23420 $this->TextField($name, $w, $h, $prop, $opt, '', '', false); 23421 if (!isset($value)) { 23422 $value = '*'; 23423 } 23424 $w = $this->GetStringWidth($value) * 2; 23425 $h *= 1.2; 23426 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255)); 23427 $jsaction = 'var f=this.getField(\''.$name.'\'); f.browseForFileToSubmit();'; 23428 $this->Button('FB_'.$name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false); 23429 break; 23430 } 23431 case 'hidden': { 23432 if (isset($value)) { 23433 $opt['v'] = $value; 23434 } 23435 $opt['f'] = array('invisible', 'hidden'); 23436 $this->TextField($name, 0, 0, $prop, $opt, '', '', false); 23437 break; 23438 } 23439 case 'image': { 23440 // THIS TYPE MUST BE FIXED 23441 if (isset($tag['attribute']['src']) AND !$this->empty_string($tag['attribute']['src'])) { 23442 $img = $tag['attribute']['src']; 23443 } else { 23444 break; 23445 } 23446 $value = 'img'; 23447 //$opt['mk'] = array('i'=>$img, 'tp'=>1, 'if'=>array('sw'=>'A', 's'=>'A', 'fb'=>false)); 23448 if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) { 23449 $jsaction = $tag['attribute']['onclick']; 23450 } else { 23451 $jsaction = ''; 23452 } 23453 $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false); 23454 break; 23455 } 23456 case 'button': { 23457 $w = $this->GetStringWidth($value) * 1.5; 23458 $h *= 1.6; 23459 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255)); 23460 if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) { 23461 $jsaction = $tag['attribute']['onclick']; 23462 } else { 23463 $jsaction = ''; 23464 } 23465 $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false); 23466 break; 23467 } 23468 } 23469 break; 23470 } 23471 case 'textarea': { 23472 $prop = array(); 23473 $opt = array(); 23474 if (isset($tag['attribute']['readonly']) AND !$this->empty_string($tag['attribute']['readonly'])) { 23475 $prop['readonly'] = true; 23476 } 23477 if (isset($tag['attribute']['name']) AND !$this->empty_string($tag['attribute']['name'])) { 23478 $name = $tag['attribute']['name']; 23479 } else { 23480 break; 23481 } 23482 if (isset($tag['attribute']['value']) AND !$this->empty_string($tag['attribute']['value'])) { 23483 $opt['v'] = $tag['attribute']['value']; 23484 } 23485 if (isset($tag['attribute']['cols']) AND !$this->empty_string($tag['attribute']['cols'])) { 23486 $w = intval($tag['attribute']['cols']) * $this->GetStringWidth(chr(32)) * 2; 23487 } else { 23488 $w = 40; 23489 } 23490 if (isset($tag['attribute']['rows']) AND !$this->empty_string($tag['attribute']['rows'])) { 23491 $h = intval($tag['attribute']['rows']) * $this->FontSize * $this->cell_height_ratio; 23492 } else { 23493 $h = 10; 23494 } 23495 $prop['multiline'] = 'true'; 23496 $this->TextField($name, $w, $h, $prop, $opt, '', '', false); 23497 break; 23498 } 23499 case 'select': { 23500 $h = $this->FontSize * $this->cell_height_ratio; 23501 if (isset($tag['attribute']['size']) AND !$this->empty_string($tag['attribute']['size'])) { 23502 $h *= ($tag['attribute']['size'] + 1); 23503 } 23504 $prop = array(); 23505 $opt = array(); 23506 if (isset($tag['attribute']['name']) AND !$this->empty_string($tag['attribute']['name'])) { 23507 $name = $tag['attribute']['name']; 23508 } else { 23509 break; 23510 } 23511 $w = 0; 23512 if (isset($tag['attribute']['opt']) AND !$this->empty_string($tag['attribute']['opt'])) { 23513 $options = explode('#!NwL!#', $tag['attribute']['opt']); 23514 $values = array(); 23515 foreach ($options as $val) { 23516 if (strpos($val, '#!TaB!#') !== false) { 23517 $opts = explode('#!TaB!#', $val); 23518 $values[] = $opts; 23519 $w = max($w, $this->GetStringWidth($opts[1])); 23520 } else { 23521 $values[] = $val; 23522 $w = max($w, $this->GetStringWidth($val)); 23523 } 23524 } 23525 } else { 23526 break; 23527 } 23528 $w *= 2; 23529 if (isset($tag['attribute']['multiple']) AND ($tag['attribute']['multiple']='multiple')) { 23530 $prop['multipleSelection'] = 'true'; 23531 $this->ListBox($name, $w, $h, $values, $prop, $opt, '', '', false); 23532 } else { 23533 $this->ComboBox($name, $w, $h, $values, $prop, $opt, '', '', false); 23534 } 23535 break; 23536 } 23537 case 'tcpdf': { 23538 if (defined('K_TCPDF_CALLS_IN_HTML') AND (K_TCPDF_CALLS_IN_HTML === true)) { 23539 // Special tag used to call TCPDF methods 23540 if (isset($tag['attribute']['method'])) { 23541 $tcpdf_method = $tag['attribute']['method']; 23542 if (method_exists($this, $tcpdf_method)) { 23543 if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) { 23544 $params = unserialize(urldecode($tag['attribute']['params'])); 23545 call_user_func_array(array($this, $tcpdf_method), $params); 23546 } else { 23547 $this->$tcpdf_method(); 23548 } 23549 $this->newline = true; 23550 } 23551 } 23552 } 23553 break; 23554 } 23555 default: { 23556 break; 23557 } 23558 } 23559 // define tags that support borders and background colors 23560 $bordertags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table'); 23561 if (in_array($tag['value'], $bordertags)) { 23562 // set border 23563 $dom[$key]['borderposition'] = $this->getBorderStartPosition(); 23564 } 23565 if ($dom[$key]['self'] AND isset($dom[$key]['attribute']['pagebreakafter'])) { 23566 $pba = $dom[$key]['attribute']['pagebreakafter']; 23567 // check for pagebreak 23568 if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) { 23569 // add a page (or trig AcceptPageBreak() for multicolumn mode) 23570 $this->checkPageBreak($this->PageBreakTrigger + 1); 23571 } 23572 if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0)))) 23573 OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) { 23574 // add a page (or trig AcceptPageBreak() for multicolumn mode) 23575 $this->checkPageBreak($this->PageBreakTrigger + 1); 23576 } 23577 } 23578 return $dom; 23579 } 23580 23590 protected function closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney=0) { 23591 $tag = $dom[$key]; 23592 $parent = $dom[($dom[$key]['parent'])]; 23593 $lasttag = ((!isset($dom[($key + 1)])) OR ((!isset($dom[($key + 2)])) AND ($dom[($key + 1)]['value'] == 'marker'))); 23594 $in_table_head = false; 23595 // maximum x position (used to draw borders) 23596 if ($this->rtl) { 23597 $xmax = $this->w; 23598 } else { 23599 $xmax = 0; 23600 } 23601 if ($tag['block']) { 23602 $hbz = 0; // distance from y to line bottom 23603 $hb = 0; // vertical space between block tags 23604 // calculate vertical space for block tags 23605 if (isset($this->tagvspaces[$tag['value']][1]['h']) AND ($this->tagvspaces[$tag['value']][1]['h'] >= 0)) { 23606 $pre_h = $this->tagvspaces[$tag['value']][1]['h']; 23607 } elseif (isset($parent['fontsize'])) { 23608 $pre_h = (($parent['fontsize'] / $this->k) * $this->cell_height_ratio); 23609 } else { 23610 $pre_h = $this->FontSize * $this->cell_height_ratio; 23611 } 23612 if (isset($this->tagvspaces[$tag['value']][1]['n'])) { 23613 $n = $this->tagvspaces[$tag['value']][1]['n']; 23614 } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) { 23615 $n = 0.6; 23616 } else { 23617 $n = 1; 23618 } 23619 if ((!isset($this->tagvspaces[$tag['value']])) AND ($tag['value'] == 'div')) { 23620 $hb = 0; 23621 } else { 23622 $hb = ($n * $pre_h); 23623 } 23624 if ($maxbottomliney > $this->PageBreakTrigger) { 23625 $hbz = ($this->FontSize * $this->cell_height_ratio); 23626 } elseif ($this->y < $maxbottomliney) { 23627 $hbz = ($maxbottomliney - $this->y); 23628 } 23629 } 23630 // Closing tag 23631 switch($tag['value']) { 23632 case 'tr': { 23633 $table_el = $dom[($dom[$key]['parent'])]['parent']; 23634 if (!isset($parent['endy'])) { 23635 $dom[($dom[$key]['parent'])]['endy'] = $this->y; 23636 $parent['endy'] = $this->y; 23637 } 23638 if (!isset($parent['endpage'])) { 23639 $dom[($dom[$key]['parent'])]['endpage'] = $this->page; 23640 $parent['endpage'] = $this->page; 23641 } 23642 if (!isset($parent['endcolumn'])) { 23643 $dom[($dom[$key]['parent'])]['endcolumn'] = $this->current_column; 23644 $parent['endcolumn'] = $this->current_column; 23645 } 23646 // update row-spanned cells 23647 if (isset($dom[$table_el]['rowspans'])) { 23648 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) { 23649 $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1; 23650 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) { 23651 if (($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) AND ($dom[$table_el]['rowspans'][$k]['endcolumn'] == $parent['endcolumn'])) { 23652 $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']); 23653 } elseif (($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) OR ($dom[$table_el]['rowspans'][$k]['endcolumn'] > $parent['endcolumn'])) { 23654 $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy']; 23655 $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage']; 23656 $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn']; 23657 } 23658 } 23659 } 23660 // report new endy and endpage to the rowspanned cells 23661 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) { 23662 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) { 23663 $dom[$table_el]['rowspans'][$k]['endpage'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']); 23664 $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage']; 23665 $dom[$table_el]['rowspans'][$k]['endcolumn'] = max($dom[$table_el]['rowspans'][$k]['endcolumn'], $dom[($dom[$key]['parent'])]['endcolumn']); 23666 $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn']; 23667 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']); 23668 $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy']; 23669 } 23670 } 23671 // update remaining rowspanned cells 23672 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) { 23673 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) { 23674 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage']; 23675 $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[($dom[$key]['parent'])]['endcolumn']; 23676 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy']; 23677 } 23678 } 23679 } 23680 $this->setPage($dom[($dom[$key]['parent'])]['endpage']); 23681 if ($this->num_columns > 1) { 23682 $this->selectColumn($dom[($dom[$key]['parent'])]['endcolumn']); 23683 } 23684 $this->y = $dom[($dom[$key]['parent'])]['endy']; 23685 if (isset($dom[$table_el]['attribute']['cellspacing'])) { 23686 $this->y += $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px'); 23687 } elseif (isset($dom[$table_el]['border-spacing'])) { 23688 $this->y += $dom[$table_el]['border-spacing']['V']; 23689 } 23690 $this->Ln(0, $cell); 23691 if ($this->current_column == $parent['startcolumn']) { 23692 $this->x = $parent['startx']; 23693 } 23694 // account for booklet mode 23695 if ($this->page > $parent['startpage']) { 23696 if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$parent['startpage']]['orm'])) { 23697 $this->x -= ($this->pagedim[$this->page]['orm'] - $this->pagedim[$parent['startpage']]['orm']); 23698 } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$parent['startpage']]['olm'])) { 23699 $this->x += ($this->pagedim[$this->page]['olm'] - $this->pagedim[$parent['startpage']]['olm']); 23700 } 23701 } 23702 break; 23703 } 23704 case 'tablehead': 23705 // closing tag used for the thead part 23706 $in_table_head = true; 23707 $this->inthead = false; 23708 case 'table': { 23709 $table_el = $parent; 23710 // set default border 23711 if (isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0)) { 23712 // set default border 23713 $border = array('LTRB' => array('width' => $this->getCSSBorderWidth($table_el['attribute']['border']), 'cap'=>'square', 'join'=>'miter', 'dash'=> 0, 'color'=>array(0,0,0))); 23714 } else { 23715 $border = 0; 23716 } 23717 $default_border = $border; 23718 // fix bottom line alignment of last line before page break 23719 foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) { 23720 // update row-spanned cells 23721 if (isset($dom[($dom[$key]['parent'])]['rowspans'])) { 23722 foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) { 23723 if ($trwsp['trid'] == $trkey) { 23724 $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1; 23725 } 23726 if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] >= 0)) { 23727 $dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey; 23728 } 23729 } 23730 } 23731 if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) { 23732 $pgendy = $this->pagedim[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim[$dom[$prevtrkey]['endpage']]['bm']; 23733 $dom[$prevtrkey]['endy'] = $pgendy; 23734 // update row-spanned cells 23735 if (isset($dom[($dom[$key]['parent'])]['rowspans'])) { 23736 foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) { 23737 if (($trwsp['trid'] == $trkey) AND ($trwsp['mrowspan'] > 1) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) { 23738 $dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy; 23739 $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1; 23740 } 23741 } 23742 } 23743 } 23744 $prevtrkey = $trkey; 23745 $table_el = $dom[($dom[$key]['parent'])]; 23746 } 23747 // for each row 23748 if (count($table_el['trids']) > 0) { 23749 unset($xmax); 23750 } 23751 foreach ($table_el['trids'] as $j => $trkey) { 23752 $parent = $dom[$trkey]; 23753 if (!isset($xmax)) { 23754 $xmax = $parent['cellpos'][(count($parent['cellpos']) - 1)]['endx']; 23755 } 23756 // for each cell on the row 23757 foreach ($parent['cellpos'] as $k => $cellpos) { 23758 if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) { 23759 $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx']; 23760 $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx']; 23761 $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy']; 23762 $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage']; 23763 $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage']; 23764 $startcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['startcolumn']; 23765 $endcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['endcolumn']; 23766 } else { 23767 $endy = $parent['endy']; 23768 $startpage = $parent['startpage']; 23769 $endpage = $parent['endpage']; 23770 $startcolumn = $parent['startcolumn']; 23771 $endcolumn = $parent['endcolumn']; 23772 } 23773 if ($this->num_columns == 0) { 23774 $this->num_columns = 1; 23775 } 23776 if (isset($cellpos['border'])) { 23777 $border = $cellpos['border']; 23778 } 23779 if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) { 23780 $this->SetFillColorArray($cellpos['bgcolor']); 23781 $fill = true; 23782 } else { 23783 $fill = false; 23784 } 23785 $x = $cellpos['startx']; 23786 $y = $parent['starty']; 23787 $starty = $y; 23788 $w = abs($cellpos['endx'] - $cellpos['startx']); 23789 // get border modes 23790 $border_start = $this->getBorderMode($border, $position='start'); 23791 $border_end = $this->getBorderMode($border, $position='end'); 23792 $border_middle = $this->getBorderMode($border, $position='middle'); 23793 // design borders around HTML cells. 23794 for ($page = $startpage; $page <= $endpage; ++$page) { // for each page 23795 $ccode = ''; 23796 $this->setPage($page); 23797 if ($this->num_columns < 2) { 23798 // single-column mode 23799 $this->x = $x; 23800 $this->y = $this->tMargin; 23801 } 23802 // account for margin changes 23803 if ($page > $startpage) { 23804 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) { 23805 $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']); 23806 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) { 23807 $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']); 23808 } 23809 } 23810 if ($startpage == $endpage) { // single page 23811 $deltacol = 0; 23812 $deltath = 0; 23813 for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column 23814 $this->selectColumn($column); 23815 if ($startcolumn == $endcolumn) { // single column 23816 $cborder = $border; 23817 $h = $endy - $parent['starty']; 23818 $this->y = $y; 23819 $this->x = $x; 23820 } elseif ($column == $startcolumn) { // first column 23821 $cborder = $border_start; 23822 $this->y = $starty; 23823 $this->x = $x; 23824 $h = $this->h - $this->y - $this->bMargin; 23825 if ($this->rtl) { 23826 $deltacol = $this->x + $this->rMargin - $this->w; 23827 } else { 23828 $deltacol = $this->x - $this->lMargin; 23829 } 23830 } elseif ($column == $endcolumn) { // end column 23831 $cborder = $border_end; 23832 if (isset($this->columns[$column]['th']['\''.$page.'\''])) { 23833 $this->y = $this->columns[$column]['th']['\''.$page.'\'']; 23834 } 23835 $this->x += $deltacol; 23836 $h = $endy - $this->y; 23837 } else { // middle column 23838 $cborder = $border_middle; 23839 if (isset($this->columns[$column]['th']['\''.$page.'\''])) { 23840 $this->y = $this->columns[$column]['th']['\''.$page.'\'']; 23841 } 23842 $this->x += $deltacol; 23843 $h = $this->h - $this->y - $this->bMargin; 23844 } 23845 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 23846 } // end for each column 23847 } elseif ($page == $startpage) { // first page 23848 $deltacol = 0; 23849 $deltath = 0; 23850 for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column 23851 $this->selectColumn($column); 23852 if ($column == $startcolumn) { // first column 23853 $cborder = $border_start; 23854 $this->y = $starty; 23855 $this->x = $x; 23856 $h = $this->h - $this->y - $this->bMargin; 23857 if ($this->rtl) { 23858 $deltacol = $this->x + $this->rMargin - $this->w; 23859 } else { 23860 $deltacol = $this->x - $this->lMargin; 23861 } 23862 } else { // middle column 23863 $cborder = $border_middle; 23864 if (isset($this->columns[$column]['th']['\''.$page.'\''])) { 23865 $this->y = $this->columns[$column]['th']['\''.$page.'\'']; 23866 } 23867 $this->x += $deltacol; 23868 $h = $this->h - $this->y - $this->bMargin; 23869 } 23870 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 23871 } // end for each column 23872 } elseif ($page == $endpage) { // last page 23873 $deltacol = 0; 23874 $deltath = 0; 23875 for ($column = 0; $column <= $endcolumn; ++$column) { // for each column 23876 $this->selectColumn($column); 23877 if ($column == $endcolumn) { // end column 23878 $cborder = $border_end; 23879 if (isset($this->columns[$column]['th']['\''.$page.'\''])) { 23880 $this->y = $this->columns[$column]['th']['\''.$page.'\'']; 23881 } 23882 $this->x += $deltacol; 23883 $h = $endy - $this->y; 23884 } else { // middle column 23885 $cborder = $border_middle; 23886 if (isset($this->columns[$column]['th']['\''.$page.'\''])) { 23887 $this->y = $this->columns[$column]['th']['\''.$page.'\'']; 23888 } 23889 $this->x += $deltacol; 23890 $h = $this->h - $this->y - $this->bMargin; 23891 } 23892 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 23893 } // end for each column 23894 } else { // middle page 23895 $deltacol = 0; 23896 $deltath = 0; 23897 for ($column = 0; $column < $this->num_columns; ++$column) { // for each column 23898 $this->selectColumn($column); 23899 $cborder = $border_middle; 23900 if (isset($this->columns[$column]['th']['\''.$page.'\''])) { 23901 $this->y = $this->columns[$column]['th']['\''.$page.'\'']; 23902 } 23903 $this->x += $deltacol; 23904 $h = $this->h - $this->y - $this->bMargin; 23905 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 23906 } // end for each column 23907 } 23908 if ($cborder OR $fill) { 23909 $offsetlen = strlen($ccode); 23910 // draw border and fill 23911 if ($this->inxobj) { 23912 // we are inside an XObject template 23913 if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) { 23914 $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']); 23915 $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey]; 23916 $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen; 23917 } else { 23918 $pagemark = $this->xobjects[$this->xobjid]['intmrk']; 23919 $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen; 23920 } 23921 $pagebuff = $this->xobjects[$this->xobjid]['outdata']; 23922 $pstart = substr($pagebuff, 0, $pagemark); 23923 $pend = substr($pagebuff, $pagemark); 23924 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend; 23925 } else { 23926 // draw border and fill 23927 if (end($this->transfmrk[$this->page]) !== false) { 23928 $pagemarkkey = key($this->transfmrk[$this->page]); 23929 $pagemark = $this->transfmrk[$this->page][$pagemarkkey]; 23930 $this->transfmrk[$this->page][$pagemarkkey] += $offsetlen; 23931 } elseif ($this->InFooter) { 23932 $pagemark = $this->footerpos[$this->page]; 23933 $this->footerpos[$this->page] += $offsetlen; 23934 } else { 23935 $pagemark = $this->intmrk[$this->page]; 23936 $this->intmrk[$this->page] += $offsetlen; 23937 } 23938 $pagebuff = $this->getPageBuffer($this->page); 23939 $pstart = substr($pagebuff, 0, $pagemark); 23940 $pend = substr($pagebuff, $pagemark); 23941 $this->setPageBuffer($this->page, $pstart.$ccode.$pend); 23942 } 23943 } 23944 } // end for each page 23945 // restore default border 23946 $border = $default_border; 23947 } // end for each cell on the row 23948 if (isset($table_el['attribute']['cellspacing'])) { 23949 $this->y += $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px'); 23950 } elseif (isset($table_el['border-spacing'])) { 23951 $this->y += $table_el['border-spacing']['V']; 23952 } 23953 $this->Ln(0, $cell); 23954 $this->x = $parent['startx']; 23955 if ($endpage > $startpage) { 23956 if (($this->rtl) AND ($this->pagedim[$endpage]['orm'] != $this->pagedim[$startpage]['orm'])) { 23957 $this->x += ($this->pagedim[$endpage]['orm'] - $this->pagedim[$startpage]['orm']); 23958 } elseif ((!$this->rtl) AND ($this->pagedim[$endpage]['olm'] != $this->pagedim[$startpage]['olm'])) { 23959 $this->x += ($this->pagedim[$endpage]['olm'] - $this->pagedim[$startpage]['olm']); 23960 } 23961 } 23962 } 23963 if (!$in_table_head) { // we are not inside a thead section 23964 $this->cell_padding = $table_el['old_cell_padding']; 23965 // reset row height 23966 $this->resetLastH(); 23967 if (($this->page == ($this->numpages - 1)) AND ($this->pageopen[$this->numpages])) { 23968 $plendiff = ($this->pagelen[$this->numpages] - $this->emptypagemrk[$this->numpages]); 23969 if (($plendiff > 0) AND ($plendiff < 60)) { 23970 $pagediff = substr($this->getPageBuffer($this->numpages), $this->emptypagemrk[$this->numpages], $plendiff); 23971 if (substr($pagediff, 0, 5) == 'BT /F') { 23972 // the difference is only a font setting 23973 $plendiff = 0; 23974 } 23975 } 23976 if ($plendiff == 0) { 23977 // remove last blank page 23978 $this->deletePage($this->numpages); 23979 } 23980 } 23981 if (isset($this->theadMargins['top'])) { 23982 // restore top margin 23983 $this->tMargin = $this->theadMargins['top']; 23984 } 23985 if (!isset($table_el['attribute']['nested']) OR ($table_el['attribute']['nested'] != 'true')) { 23986 // reset main table header 23987 $this->thead = ''; 23988 $this->theadMargins = array(); 23989 $this->pagedim[$this->page]['tm'] = $this->tMargin; 23990 } 23991 } 23992 $parent = $table_el; 23993 break; 23994 } 23995 case 'a': { 23996 $this->HREF = ''; 23997 break; 23998 } 23999 case 'sup': { 24000 $this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k)); 24001 break; 24002 } 24003 case 'sub': { 24004 $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize'])/$this->k)); 24005 break; 24006 } 24007 case 'div': { 24008 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); 24009 break; 24010 } 24011 case 'blockquote': { 24012 if ($this->rtl) { 24013 $this->rMargin -= $this->listindent; 24014 } else { 24015 $this->lMargin -= $this->listindent; 24016 } 24017 --$this->listindentlevel; 24018 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); 24019 break; 24020 } 24021 case 'p': { 24022 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); 24023 break; 24024 } 24025 case 'pre': { 24026 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); 24027 $this->premode = false; 24028 break; 24029 } 24030 case 'dl': { 24031 --$this->listnum; 24032 if ($this->listnum <= 0) { 24033 $this->listnum = 0; 24034 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); 24035 } else { 24036 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag); 24037 } 24038 $this->resetLastH(); 24039 break; 24040 } 24041 case 'dt': { 24042 $this->lispacer = ''; 24043 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag); 24044 break; 24045 } 24046 case 'dd': { 24047 $this->lispacer = ''; 24048 if ($this->rtl) { 24049 $this->rMargin -= $this->listindent; 24050 } else { 24051 $this->lMargin -= $this->listindent; 24052 } 24053 --$this->listindentlevel; 24054 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag); 24055 break; 24056 } 24057 case 'ul': 24058 case 'ol': { 24059 --$this->listnum; 24060 $this->lispacer = ''; 24061 if ($this->rtl) { 24062 $this->rMargin -= $this->listindent; 24063 } else { 24064 $this->lMargin -= $this->listindent; 24065 } 24066 --$this->listindentlevel; 24067 if ($this->listnum <= 0) { 24068 $this->listnum = 0; 24069 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); 24070 } else { 24071 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag); 24072 } 24073 $this->resetLastH(); 24074 break; 24075 } 24076 case 'li': { 24077 $this->lispacer = ''; 24078 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag); 24079 break; 24080 } 24081 case 'h1': 24082 case 'h2': 24083 case 'h3': 24084 case 'h4': 24085 case 'h5': 24086 case 'h6': { 24087 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); 24088 break; 24089 } 24090 // Form fields (since 4.8.000 - 2009-09-07) 24091 case 'form': { 24092 $this->form_action = ''; 24093 $this->form_enctype = 'application/x-www-form-urlencoded'; 24094 break; 24095 } 24096 default : { 24097 break; 24098 } 24099 } 24100 // draw border and background (if any) 24101 $this->drawHTMLTagBorder($parent, $xmax); 24102 if (isset($dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'])) { 24103 $pba = $dom[($dom[$key]['parent'])]['attribute']['pagebreakafter']; 24104 // check for pagebreak 24105 if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) { 24106 // add a page (or trig AcceptPageBreak() for multicolumn mode) 24107 $this->checkPageBreak($this->PageBreakTrigger + 1); 24108 } 24109 if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0)))) 24110 OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) { 24111 // add a page (or trig AcceptPageBreak() for multicolumn mode) 24112 $this->checkPageBreak($this->PageBreakTrigger + 1); 24113 } 24114 } 24115 $this->tmprtl = false; 24116 return $dom; 24117 } 24118 24128 protected function addHTMLVertSpace($hbz=0, $hb=0, $cell=false, $firsttag=false, $lasttag=false) { 24129 if ($firsttag) { 24130 $this->Ln(0, $cell); 24131 $this->htmlvspace = 0; 24132 return; 24133 } 24134 if ($lasttag) { 24135 $this->Ln($hbz, $cell); 24136 $this->htmlvspace = 0; 24137 return; 24138 } 24139 if ($hb < $this->htmlvspace) { 24140 $hd = 0; 24141 } else { 24142 $hd = $hb - $this->htmlvspace; 24143 $this->htmlvspace = $hb; 24144 } 24145 $this->Ln(($hbz + $hd), $cell); 24146 } 24147 24154 protected function getBorderStartPosition() { 24155 if ($this->rtl) { 24156 $xmax = $this->lMargin; 24157 } else { 24158 $xmax = $this->w - $this->rMargin; 24159 } 24160 return array('page' => $this->page, 'column' => $this->current_column, 'x' => $this->x, 'y' => $this->y, 'xmax' => $xmax); 24161 } 24162 24170 protected function drawHTMLTagBorder($tag, $xmax) { 24171 if (!isset($tag['borderposition'])) { 24172 // nothing to draw 24173 return; 24174 } 24175 $prev_x = $this->x; 24176 $prev_y = $this->y; 24177 $prev_lasth = $this->lasth; 24178 $border = 0; 24179 $fill = false; 24180 $this->lasth = 0; 24181 if (isset($tag['border']) AND !empty($tag['border'])) { 24182 // get border style 24183 $border = $tag['border']; 24184 if (!$this->empty_string($this->thead) AND (!$this->inthead)) { 24185 // border for table header 24186 $border = $this->getBorderMode($border, $position='middle'); 24187 } 24188 } 24189 if (isset($tag['bgcolor']) AND ($tag['bgcolor'] !== false)) { 24190 // get background color 24191 $old_bgcolor = $this->bgcolor; 24192 $this->SetFillColorArray($tag['bgcolor']); 24193 $fill = true; 24194 } 24195 if (!$border AND !$fill) { 24196 // nothing to draw 24197 return; 24198 } 24199 if (isset($tag['attribute']['cellspacing'])) { 24200 $clsp = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px'); 24201 $cellspacing = array('H' => $clsp, 'V' => $clsp); 24202 } elseif (isset($tag['border-spacing'])) { 24203 $cellspacing = $tag['border-spacing']; 24204 } else { 24205 $cellspacing = array('H' => 0, 'V' => 0); 24206 } 24207 if (($tag['value'] != 'table') AND (is_array($border)) AND (!empty($border))) { 24208 // draw the border externally respect the sqare edge. 24209 $border['mode'] = 'ext'; 24210 } 24211 if ($this->rtl) { 24212 if ($xmax >= $tag['borderposition']['x']) { 24213 $xmax = $tag['borderposition']['xmax']; 24214 } 24215 $w = ($tag['borderposition']['x'] - $xmax); 24216 } else { 24217 if ($xmax <= $tag['borderposition']['x']) { 24218 $xmax = $tag['borderposition']['xmax']; 24219 } 24220 $w = ($xmax - $tag['borderposition']['x']); 24221 } 24222 if ($w <= 0) { 24223 return; 24224 } 24225 $w += $cellspacing['H']; 24226 $startpage = $tag['borderposition']['page']; 24227 $startcolumn = $tag['borderposition']['column']; 24228 $x = $tag['borderposition']['x']; 24229 $y = $tag['borderposition']['y']; 24230 $endpage = $this->page; 24231 $starty = $tag['borderposition']['y'] - $cellspacing['V']; 24232 $currentY = $this->y; 24233 $this->x = $x; 24234 // get latest column 24235 $endcolumn = $this->current_column; 24236 if ($this->num_columns == 0) { 24237 $this->num_columns = 1; 24238 } 24239 // get border modes 24240 $border_start = $this->getBorderMode($border, $position='start'); 24241 $border_end = $this->getBorderMode($border, $position='end'); 24242 $border_middle = $this->getBorderMode($border, $position='middle'); 24243 // temporary disable page regions 24244 $temp_page_regions = $this->page_regions; 24245 $this->page_regions = array(); 24246 // design borders around HTML cells. 24247 for ($page = $startpage; $page <= $endpage; ++$page) { // for each page 24248 $ccode = ''; 24249 $this->setPage($page); 24250 if ($this->num_columns < 2) { 24251 // single-column mode 24252 $this->x = $x; 24253 $this->y = $this->tMargin; 24254 } 24255 // account for margin changes 24256 if ($page > $startpage) { 24257 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) { 24258 $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']); 24259 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) { 24260 $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']); 24261 } 24262 } 24263 if ($startpage == $endpage) { 24264 // single page 24265 for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column 24266 $this->selectColumn($column); 24267 if ($startcolumn == $endcolumn) { // single column 24268 $cborder = $border; 24269 $h = ($currentY - $y) + $cellspacing['V']; 24270 $this->y = $starty; 24271 } elseif ($column == $startcolumn) { // first column 24272 $cborder = $border_start; 24273 $this->y = $starty; 24274 $h = $this->h - $this->y - $this->bMargin; 24275 } elseif ($column == $endcolumn) { // end column 24276 $cborder = $border_end; 24277 $h = $currentY - $this->y; 24278 } else { // middle column 24279 $cborder = $border_middle; 24280 $h = $this->h - $this->y - $this->bMargin; 24281 } 24282 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 24283 } // end for each column 24284 } elseif ($page == $startpage) { // first page 24285 for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column 24286 $this->selectColumn($column); 24287 if ($column == $startcolumn) { // first column 24288 $cborder = $border_start; 24289 $this->y = $starty; 24290 $h = $this->h - $this->y - $this->bMargin; 24291 } else { // middle column 24292 $cborder = $border_middle; 24293 $h = $this->h - $this->y - $this->bMargin; 24294 } 24295 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 24296 } // end for each column 24297 } elseif ($page == $endpage) { // last page 24298 for ($column = 0; $column <= $endcolumn; ++$column) { // for each column 24299 $this->selectColumn($column); 24300 if ($column == $endcolumn) { 24301 // end column 24302 $cborder = $border_end; 24303 $h = $currentY - $this->y; 24304 } else { 24305 // middle column 24306 $cborder = $border_middle; 24307 $h = $this->h - $this->y - $this->bMargin; 24308 } 24309 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 24310 } // end for each column 24311 } else { // middle page 24312 for ($column = 0; $column < $this->num_columns; ++$column) { // for each column 24313 $this->selectColumn($column); 24314 $cborder = $border_middle; 24315 $h = $this->h - $this->y - $this->bMargin; 24316 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; 24317 } // end for each column 24318 } 24319 if ($cborder OR $fill) { 24320 $offsetlen = strlen($ccode); 24321 // draw border and fill 24322 if ($this->inxobj) { 24323 // we are inside an XObject template 24324 if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) { 24325 $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']); 24326 $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey]; 24327 $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen; 24328 } else { 24329 $pagemark = $this->xobjects[$this->xobjid]['intmrk']; 24330 $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen; 24331 } 24332 $pagebuff = $this->xobjects[$this->xobjid]['outdata']; 24333 $pstart = substr($pagebuff, 0, $pagemark); 24334 $pend = substr($pagebuff, $pagemark); 24335 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend; 24336 } else { 24337 if (end($this->transfmrk[$this->page]) !== false) { 24338 $pagemarkkey = key($this->transfmrk[$this->page]); 24339 $pagemark = $this->transfmrk[$this->page][$pagemarkkey]; 24340 $this->transfmrk[$this->page][$pagemarkkey] += $offsetlen; 24341 } elseif ($this->InFooter) { 24342 $pagemark = $this->footerpos[$this->page]; 24343 $this->footerpos[$this->page] += $offsetlen; 24344 } else { 24345 $pagemark = $this->intmrk[$this->page]; 24346 $this->intmrk[$this->page] += $offsetlen; 24347 } 24348 $pagebuff = $this->getPageBuffer($this->page); 24349 $pstart = substr($pagebuff, 0, $this->bordermrk[$this->page]); 24350 $pend = substr($pagebuff, $this->bordermrk[$this->page]); 24351 $this->setPageBuffer($this->page, $pstart.$ccode.$pend); 24352 $this->bordermrk[$this->page] += $offsetlen; 24353 $this->cntmrk[$this->page] += $offsetlen; 24354 } 24355 } 24356 } // end for each page 24357 // restore page regions 24358 $this->page_regions = $temp_page_regions; 24359 if (isset($old_bgcolor)) { 24360 // restore background color 24361 $this->SetFillColorArray($old_bgcolor); 24362 } 24363 // restore pointer position 24364 $this->x = $prev_x; 24365 $this->y = $prev_y; 24366 $this->lasth = $prev_lasth; 24367 } 24368 24375 public function setLIsymbol($symbol='!') { 24376 // check for custom image symbol 24377 if (substr($symbol, 0, 4) == 'img|') { 24378 $this->lisymbol = $symbol; 24379 return; 24380 } 24381 $symbol = strtolower($symbol); 24382 switch ($symbol) { 24383 case '!' : 24384 case '#' : 24385 case 'disc' : 24386 case 'circle' : 24387 case 'square' : 24388 case '1': 24389 case 'decimal': 24390 case 'decimal-leading-zero': 24391 case 'i': 24392 case 'lower-roman': 24393 case 'I': 24394 case 'upper-roman': 24395 case 'a': 24396 case 'lower-alpha': 24397 case 'lower-latin': 24398 case 'A': 24399 case 'upper-alpha': 24400 case 'upper-latin': 24401 case 'lower-greek': { 24402 $this->lisymbol = $symbol; 24403 break; 24404 } 24405 default : { 24406 $this->lisymbol = ''; 24407 } 24408 } 24409 } 24410 24419 public function SetBooklet($booklet=true, $inner=-1, $outer=-1) { 24420 $this->booklet = $booklet; 24421 if ($inner >= 0) { 24422 $this->lMargin = $inner; 24423 } 24424 if ($outer >= 0) { 24425 $this->rMargin = $outer; 24426 } 24427 } 24428 24435 protected function swapMargins($reverse=true) { 24436 if ($reverse) { 24437 // swap left and right margins 24438 $mtemp = $this->original_lMargin; 24439 $this->original_lMargin = $this->original_rMargin; 24440 $this->original_rMargin = $mtemp; 24441 $deltam = $this->original_lMargin - $this->original_rMargin; 24442 $this->lMargin += $deltam; 24443 $this->rMargin -= $deltam; 24444 } 24445 } 24446 24459 public function setHtmlVSpace($tagvs) { 24460 $this->tagvspaces = $tagvs; 24461 } 24462 24469 public function setListIndentWidth($width) { 24470 return $this->customlistindent = floatval($width); 24471 } 24472 24479 public function setOpenCell($isopen) { 24480 $this->opencell = $isopen; 24481 } 24482 24490 public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') { 24491 $this->htmlLinkColorArray = $color; 24492 $this->htmlLinkFontStyle = $fontstyle; 24493 } 24494 24505 public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) { 24506 $supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt'); 24507 $retval = 0; 24508 $value = 0; 24509 $unit = 'px'; 24510 $k = $this->k; 24511 if ($points) { 24512 $k = 1; 24513 } 24514 if (in_array($defaultunit, $supportedunits)) { 24515 $unit = $defaultunit; 24516 } 24517 if (is_numeric($htmlval)) { 24518 $value = floatval($htmlval); 24519 } elseif (preg_match('/([0-9\.\-\+]+)/', $htmlval, $mnum)) { 24520 $value = floatval($mnum[1]); 24521 if (preg_match('/([a-z%]+)/', $htmlval, $munit)) { 24522 if (in_array($munit[1], $supportedunits)) { 24523 $unit = $munit[1]; 24524 } 24525 } 24526 } 24527 switch ($unit) { 24528 // percentage 24529 case '%': { 24530 $retval = (($value * $refsize) / 100); 24531 break; 24532 } 24533 // relative-size 24534 case 'em': { 24535 $retval = ($value * $refsize); 24536 break; 24537 } 24538 // height of lower case 'x' (about half the font-size) 24539 case 'ex': { 24540 $retval = $value * ($refsize / 2); 24541 break; 24542 } 24543 // absolute-size 24544 case 'in': { 24545 $retval = ($value * $this->dpi) / $k; 24546 break; 24547 } 24548 // centimeters 24549 case 'cm': { 24550 $retval = ($value / 2.54 * $this->dpi) / $k; 24551 break; 24552 } 24553 // millimeters 24554 case 'mm': { 24555 $retval = ($value / 25.4 * $this->dpi) / $k; 24556 break; 24557 } 24558 // one pica is 12 points 24559 case 'pc': { 24560 $retval = ($value * 12) / $k; 24561 break; 24562 } 24563 // points 24564 case 'pt': { 24565 $retval = $value / $k; 24566 break; 24567 } 24568 // pixels 24569 case 'px': { 24570 $retval = $this->pixelsToUnits($value); 24571 break; 24572 } 24573 } 24574 return $retval; 24575 } 24576 24584 public function intToRoman($number) { 24585 $roman = ''; 24586 while ($number >= 1000) { 24587 $roman .= 'M'; 24588 $number -= 1000; 24589 } 24590 while ($number >= 900) { 24591 $roman .= 'CM'; 24592 $number -= 900; 24593 } 24594 while ($number >= 500) { 24595 $roman .= 'D'; 24596 $number -= 500; 24597 } 24598 while ($number >= 400) { 24599 $roman .= 'CD'; 24600 $number -= 400; 24601 } 24602 while ($number >= 100) { 24603 $roman .= 'C'; 24604 $number -= 100; 24605 } 24606 while ($number >= 90) { 24607 $roman .= 'XC'; 24608 $number -= 90; 24609 } 24610 while ($number >= 50) { 24611 $roman .= 'L'; 24612 $number -= 50; 24613 } 24614 while ($number >= 40) { 24615 $roman .= 'XL'; 24616 $number -= 40; 24617 } 24618 while ($number >= 10) { 24619 $roman .= 'X'; 24620 $number -= 10; 24621 } 24622 while ($number >= 9) { 24623 $roman .= 'IX'; 24624 $number -= 9; 24625 } 24626 while ($number >= 5) { 24627 $roman .= 'V'; 24628 $number -= 5; 24629 } 24630 while ($number >= 4) { 24631 $roman .= 'IV'; 24632 $number -= 4; 24633 } 24634 while ($number >= 1) { 24635 $roman .= 'I'; 24636 --$number; 24637 } 24638 return $roman; 24639 } 24640 24649 protected function putHtmlListBullet($listdepth, $listtype='', $size=10) { 24650 $size /= $this->k; 24651 $fill = ''; 24652 $bgcolor = $this->bgcolor; 24653 $color = $this->fgcolor; 24654 $strokecolor = $this->strokecolor; 24655 $width = 0; 24656 $textitem = ''; 24657 $tmpx = $this->x; 24658 $lspace = $this->GetStringWidth(' '); 24659 if ($listtype == '^') { 24660 // special symbol used for avoid justification of rect bullet 24661 $this->lispacer = ''; 24662 return; 24663 } elseif ($listtype == '!') { 24664 // set default list type for unordered list 24665 $deftypes = array('disc', 'circle', 'square'); 24666 $listtype = $deftypes[($listdepth - 1) % 3]; 24667 } elseif ($listtype == '#') { 24668 // set default list type for ordered list 24669 $listtype = 'decimal'; 24670 } elseif (substr($listtype, 0, 4) == 'img|') { 24671 // custom image type ('img|type|width|height|image.ext') 24672 $img = explode('|', $listtype); 24673 $listtype = 'img'; 24674 } 24675 switch ($listtype) { 24676 // unordered types 24677 case 'none': { 24678 break; 24679 } 24680 case 'disc': { 24681 $r = $size / 6; 24682 $lspace += (2 * $r); 24683 if ($this->rtl) { 24684 $this->x += $lspace; 24685 } else { 24686 $this->x -= $lspace; 24687 } 24688 $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), $r, 0, 360, 'F', array(), $color, 8); 24689 break; 24690 } 24691 case 'circle': { 24692 $r = $size / 6; 24693 $lspace += (2 * $r); 24694 if ($this->rtl) { 24695 $this->x += $lspace; 24696 } else { 24697 $this->x -= $lspace; 24698 } 24699 $prev_line_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor; 24700 $new_line_style = array('width' => ($r / 3), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'phase' => 0, 'color'=>$color); 24701 $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), ($r * (1 - (1/6))), 0, 360, 'D', $new_line_style, array(), 8); 24702 $this->_out($prev_line_style); // restore line settings 24703 break; 24704 } 24705 case 'square': { 24706 $l = $size / 3; 24707 $lspace += $l; 24708 if ($this->rtl) {; 24709 $this->x += $lspace; 24710 } else { 24711 $this->x -= $lspace; 24712 } 24713 $this->Rect($this->x, ($this->y + (($this->lasth - $l) / 2)), $l, $l, 'F', array(), $color); 24714 break; 24715 } 24716 case 'img': { 24717 // 1=>type, 2=>width, 3=>height, 4=>image.ext 24718 $lspace += $img[2]; 24719 if ($this->rtl) {; 24720 $this->x += $lspace; 24721 } else { 24722 $this->x -= $lspace; 24723 } 24724 $imgtype = strtolower($img[1]); 24725 $prev_y = $this->y; 24726 switch ($imgtype) { 24727 case 'svg': { 24728 $this->ImageSVG($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', 'T', '', 0, false); 24729 break; 24730 } 24731 case 'ai': 24732 case 'eps': { 24733 $this->ImageEps($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', true, 'T', '', 0, false); 24734 break; 24735 } 24736 default: { 24737 $this->Image($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], $img[1], '', 'T', false, 300, '', false, false, 0, false, false, false); 24738 break; 24739 } 24740 } 24741 $this->y = $prev_y; 24742 break; 24743 } 24744 // ordered types 24745 // $this->listcount[$this->listnum]; 24746 // $textitem 24747 case '1': 24748 case 'decimal': { 24749 $textitem = $this->listcount[$this->listnum]; 24750 break; 24751 } 24752 case 'decimal-leading-zero': { 24753 $textitem = sprintf('%02d', $this->listcount[$this->listnum]); 24754 break; 24755 } 24756 case 'i': 24757 case 'lower-roman': { 24758 $textitem = strtolower($this->intToRoman($this->listcount[$this->listnum])); 24759 break; 24760 } 24761 case 'I': 24762 case 'upper-roman': { 24763 $textitem = $this->intToRoman($this->listcount[$this->listnum]); 24764 break; 24765 } 24766 case 'a': 24767 case 'lower-alpha': 24768 case 'lower-latin': { 24769 $textitem = chr(97 + $this->listcount[$this->listnum] - 1); 24770 break; 24771 } 24772 case 'A': 24773 case 'upper-alpha': 24774 case 'upper-latin': { 24775 $textitem = chr(65 + $this->listcount[$this->listnum] - 1); 24776 break; 24777 } 24778 case 'lower-greek': { 24779 $textitem = $this->unichr(945 + $this->listcount[$this->listnum] - 1); 24780 break; 24781 } 24782 /* 24783 // Types to be implemented (special handling) 24784 case 'hebrew': { 24785 break; 24786 } 24787 case 'armenian': { 24788 break; 24789 } 24790 case 'georgian': { 24791 break; 24792 } 24793 case 'cjk-ideographic': { 24794 break; 24795 } 24796 case 'hiragana': { 24797 break; 24798 } 24799 case 'katakana': { 24800 break; 24801 } 24802 case 'hiragana-iroha': { 24803 break; 24804 } 24805 case 'katakana-iroha': { 24806 break; 24807 } 24808 */ 24809 default: { 24810 $textitem = $this->listcount[$this->listnum]; 24811 } 24812 } 24813 if (!$this->empty_string($textitem)) { 24814 // Check whether we need a new page or new column 24815 $prev_y = $this->y; 24816 $h = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B']; 24817 if ($this->checkPageBreak($h) OR ($this->y < $prev_y)) { 24818 $tmpx = $this->x; 24819 } 24820 // print ordered item 24821 if ($this->rtl) { 24822 $textitem = '.'.$textitem; 24823 } else { 24824 $textitem = $textitem.'.'; 24825 } 24826 $lspace += $this->GetStringWidth($textitem); 24827 if ($this->rtl) { 24828 $this->x += $lspace; 24829 } else { 24830 $this->x -= $lspace; 24831 } 24832 $this->Write($this->lasth, $textitem, '', false, '', false, 0, false); 24833 } 24834 $this->x = $tmpx; 24835 $this->lispacer = '^'; 24836 // restore colors 24837 $this->SetFillColorArray($bgcolor); 24838 $this->SetDrawColorArray($strokecolor); 24839 $this->SettextColorArray($color); 24840 } 24841 24848 protected function getGraphicVars() { 24849 $grapvars = array( 24850 'FontFamily' => $this->FontFamily, 24851 'FontStyle' => $this->FontStyle, 24852 'FontSizePt' => $this->FontSizePt, 24853 'rMargin' => $this->rMargin, 24854 'lMargin' => $this->lMargin, 24855 'cell_padding' => $this->cell_padding, 24856 'cell_margin' => $this->cell_margin, 24857 'LineWidth' => $this->LineWidth, 24858 'linestyleWidth' => $this->linestyleWidth, 24859 'linestyleCap' => $this->linestyleCap, 24860 'linestyleJoin' => $this->linestyleJoin, 24861 'linestyleDash' => $this->linestyleDash, 24862 'textrendermode' => $this->textrendermode, 24863 'textstrokewidth' => $this->textstrokewidth, 24864 'DrawColor' => $this->DrawColor, 24865 'FillColor' => $this->FillColor, 24866 'TextColor' => $this->TextColor, 24867 'ColorFlag' => $this->ColorFlag, 24868 'bgcolor' => $this->bgcolor, 24869 'fgcolor' => $this->fgcolor, 24870 'htmlvspace' => $this->htmlvspace, 24871 'listindent' => $this->listindent, 24872 'listindentlevel' => $this->listindentlevel, 24873 'listnum' => $this->listnum, 24874 'listordered' => $this->listordered, 24875 'listcount' => $this->listcount, 24876 'lispacer' => $this->lispacer, 24877 'cell_height_ratio' => $this->cell_height_ratio, 24878 'font_stretching' => $this->font_stretching, 24879 'font_spacing' => $this->font_spacing, 24880 // extended 24881 'lasth' => $this->lasth, 24882 'tMargin' => $this->tMargin, 24883 'bMargin' => $this->bMargin, 24884 'AutoPageBreak' => $this->AutoPageBreak, 24885 'PageBreakTrigger' => $this->PageBreakTrigger, 24886 'x' => $this->x, 24887 'y' => $this->y, 24888 'w' => $this->w, 24889 'h' => $this->h, 24890 'wPt' => $this->wPt, 24891 'hPt' => $this->hPt, 24892 'fwPt' => $this->fwPt, 24893 'fhPt' => $this->fhPt, 24894 'page' => $this->page, 24895 'current_column' => $this->current_column, 24896 'num_columns' => $this->num_columns 24897 ); 24898 return $grapvars; 24899 } 24900 24908 protected function setGraphicVars($gvars, $extended=false) { 24909 $this->FontFamily = $gvars['FontFamily']; 24910 $this->FontStyle = $gvars['FontStyle']; 24911 $this->FontSizePt = $gvars['FontSizePt']; 24912 $this->rMargin = $gvars['rMargin']; 24913 $this->lMargin = $gvars['lMargin']; 24914 $this->cell_padding = $gvars['cell_padding']; 24915 $this->cell_margin = $gvars['cell_margin']; 24916 $this->LineWidth = $gvars['LineWidth']; 24917 $this->linestyleWidth = $gvars['linestyleWidth']; 24918 $this->linestyleCap = $gvars['linestyleCap']; 24919 $this->linestyleJoin = $gvars['linestyleJoin']; 24920 $this->linestyleDash = $gvars['linestyleDash']; 24921 $this->textrendermode = $gvars['textrendermode']; 24922 $this->textstrokewidth = $gvars['textstrokewidth']; 24923 $this->DrawColor = $gvars['DrawColor']; 24924 $this->FillColor = $gvars['FillColor']; 24925 $this->TextColor = $gvars['TextColor']; 24926 $this->ColorFlag = $gvars['ColorFlag']; 24927 $this->bgcolor = $gvars['bgcolor']; 24928 $this->fgcolor = $gvars['fgcolor']; 24929 $this->htmlvspace = $gvars['htmlvspace']; 24930 $this->listindent = $gvars['listindent']; 24931 $this->listindentlevel = $gvars['listindentlevel']; 24932 $this->listnum = $gvars['listnum']; 24933 $this->listordered = $gvars['listordered']; 24934 $this->listcount = $gvars['listcount']; 24935 $this->lispacer = $gvars['lispacer']; 24936 $this->cell_height_ratio = $gvars['cell_height_ratio']; 24937 $this->font_stretching = $gvars['font_stretching']; 24938 $this->font_spacing = $gvars['font_spacing']; 24939 if ($extended) { 24940 // restore extended values 24941 $this->lasth = $gvars['lasth']; 24942 $this->tMargin = $gvars['tMargin']; 24943 $this->bMargin = $gvars['bMargin']; 24944 $this->AutoPageBreak = $gvars['AutoPageBreak']; 24945 $this->PageBreakTrigger = $gvars['PageBreakTrigger']; 24946 $this->x = $gvars['x']; 24947 $this->y = $gvars['y']; 24948 $this->w = $gvars['w']; 24949 $this->h = $gvars['h']; 24950 $this->wPt = $gvars['wPt']; 24951 $this->hPt = $gvars['hPt']; 24952 $this->fwPt = $gvars['fwPt']; 24953 $this->fhPt = $gvars['fhPt']; 24954 $this->page = $gvars['page']; 24955 $this->current_column = $gvars['current_column']; 24956 $this->num_columns = $gvars['num_columns']; 24957 } 24958 $this->_out(''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor.''); 24959 if (!$this->empty_string($this->FontFamily)) { 24960 $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt); 24961 } 24962 } 24963 24971 protected function getObjFilename($name) { 24972 return tempnam(K_PATH_CACHE, $name.'_'); 24973 } 24974 24983 protected function writeDiskCache($filename, $data, $append=false) { 24984 if ($append) { 24985 $fmode = 'ab+'; 24986 } else { 24987 $fmode = 'wb+'; 24988 } 24989 $f = @fopen($filename, $fmode); 24990 if (!$f) { 24991 $this->Error('Unable to write cache file: '.$filename); 24992 } else { 24993 fwrite($f, $data); 24994 fclose($f); 24995 } 24996 // update file length (needed for transactions) 24997 if (!isset($this->cache_file_length['_'.$filename])) { 24998 $this->cache_file_length['_'.$filename] = strlen($data); 24999 } else { 25000 $this->cache_file_length['_'.$filename] += strlen($data); 25001 } 25002 } 25003 25011 protected function readDiskCache($filename) { 25012 return file_get_contents($filename); 25013 } 25014 25021 protected function setBuffer($data) { 25022 $this->bufferlen += strlen($data); 25023 if ($this->diskcache) { 25024 if (!isset($this->buffer) OR $this->empty_string($this->buffer)) { 25025 $this->buffer = $this->getObjFilename('buffer'); 25026 } 25027 $this->writeDiskCache($this->buffer, $data, true); 25028 } else { 25029 $this->buffer .= $data; 25030 } 25031 } 25032 25039 protected function replaceBuffer($data) { 25040 $this->bufferlen = strlen($data); 25041 if ($this->diskcache) { 25042 if (!isset($this->buffer) OR $this->empty_string($this->buffer)) { 25043 $this->buffer = $this->getObjFilename('buffer'); 25044 } 25045 $this->writeDiskCache($this->buffer, $data, false); 25046 } else { 25047 $this->buffer = $data; 25048 } 25049 } 25050 25057 protected function getBuffer() { 25058 if ($this->diskcache) { 25059 return $this->readDiskCache($this->buffer); 25060 } else { 25061 return $this->buffer; 25062 } 25063 } 25064 25073 protected function setPageBuffer($page, $data, $append=false) { 25074 if ($this->diskcache) { 25075 if (!isset($this->pages[$page])) { 25076 $this->pages[$page] = $this->getObjFilename('page'.$page); 25077 } 25078 $this->writeDiskCache($this->pages[$page], $data, $append); 25079 } else { 25080 if ($append) { 25081 $this->pages[$page] .= $data; 25082 } else { 25083 $this->pages[$page] = $data; 25084 } 25085 } 25086 if ($append AND isset($this->pagelen[$page])) { 25087 $this->pagelen[$page] += strlen($data); 25088 } else { 25089 $this->pagelen[$page] = strlen($data); 25090 } 25091 } 25092 25100 protected function getPageBuffer($page) { 25101 if ($this->diskcache) { 25102 return $this->readDiskCache($this->pages[$page]); 25103 } elseif (isset($this->pages[$page])) { 25104 return $this->pages[$page]; 25105 } 25106 return false; 25107 } 25108 25116 protected function setImageBuffer($image, $data) { 25117 if ($this->diskcache) { 25118 if (!isset($this->images[$image])) { 25119 $this->images[$image] = $this->getObjFilename('image'.$image); 25120 } 25121 $this->writeDiskCache($this->images[$image], serialize($data)); 25122 } else { 25123 $this->images[$image] = $data; 25124 } 25125 if (!in_array($image, $this->imagekeys)) { 25126 $this->imagekeys[] = $image; 25127 ++$this->numimages; 25128 } 25129 } 25130 25139 protected function setImageSubBuffer($image, $key, $data) { 25140 if (!isset($this->images[$image])) { 25141 $this->setImageBuffer($image, array()); 25142 } 25143 if ($this->diskcache) { 25144 $tmpimg = $this->getImageBuffer($image); 25145 $tmpimg[$key] = $data; 25146 $this->writeDiskCache($this->images[$image], serialize($tmpimg)); 25147 } else { 25148 $this->images[$image][$key] = $data; 25149 } 25150 } 25151 25159 protected function getImageBuffer($image) { 25160 if ($this->diskcache AND isset($this->images[$image])) { 25161 return unserialize($this->readDiskCache($this->images[$image])); 25162 } elseif (isset($this->images[$image])) { 25163 return $this->images[$image]; 25164 } 25165 return false; 25166 } 25167 25175 protected function setFontBuffer($font, $data) { 25176 if ($this->diskcache) { 25177 if (!isset($this->fonts[$font])) { 25178 $this->fonts[$font] = $this->getObjFilename('font'); 25179 } 25180 $this->writeDiskCache($this->fonts[$font], serialize($data)); 25181 } else { 25182 $this->fonts[$font] = $data; 25183 } 25184 if (!in_array($font, $this->fontkeys)) { 25185 $this->fontkeys[] = $font; 25186 // store object ID for current font 25187 ++$this->n; 25188 $this->font_obj_ids[$font] = $this->n; 25189 $this->setFontSubBuffer($font, 'n', $this->n); 25190 } 25191 } 25192 25201 protected function setFontSubBuffer($font, $key, $data) { 25202 if (!isset($this->fonts[$font])) { 25203 $this->setFontBuffer($font, array()); 25204 } 25205 if ($this->diskcache) { 25206 $tmpfont = $this->getFontBuffer($font); 25207 $tmpfont[$key] = $data; 25208 $this->writeDiskCache($this->fonts[$font], serialize($tmpfont)); 25209 } else { 25210 $this->fonts[$font][$key] = $data; 25211 } 25212 } 25213 25221 protected function getFontBuffer($font) { 25222 if ($this->diskcache AND isset($this->fonts[$font])) { 25223 return unserialize($this->readDiskCache($this->fonts[$font])); 25224 } elseif (isset($this->fonts[$font])) { 25225 return $this->fonts[$font]; 25226 } 25227 return false; 25228 } 25229 25238 public function movePage($frompage, $topage) { 25239 if (($frompage > $this->numpages) OR ($frompage <= $topage)) { 25240 return false; 25241 } 25242 if ($frompage == $this->page) { 25243 // close the page before moving it 25244 $this->endPage(); 25245 } 25246 // move all page-related states 25247 $tmppage = $this->getPageBuffer($frompage); 25248 $tmppagedim = $this->pagedim[$frompage]; 25249 $tmppagelen = $this->pagelen[$frompage]; 25250 $tmpintmrk = $this->intmrk[$frompage]; 25251 $tmpbordermrk = $this->bordermrk[$frompage]; 25252 $tmpcntmrk = $this->cntmrk[$frompage]; 25253 if (isset($this->footerpos[$frompage])) { 25254 $tmpfooterpos = $this->footerpos[$frompage]; 25255 } 25256 if (isset($this->footerlen[$frompage])) { 25257 $tmpfooterlen = $this->footerlen[$frompage]; 25258 } 25259 if (isset($this->transfmrk[$frompage])) { 25260 $tmptransfmrk = $this->transfmrk[$frompage]; 25261 } 25262 if (isset($this->PageAnnots[$frompage])) { 25263 $tmpannots = $this->PageAnnots[$frompage]; 25264 } 25265 if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) { 25266 for ($i = $frompage; $i > $topage; --$i) { 25267 if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $frompage)) { 25268 --$this->pagegroups[$this->newpagegroup[$i]]; 25269 break; 25270 } 25271 } 25272 for ($i = $topage; $i > 0; --$i) { 25273 if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $topage)) { 25274 ++$this->pagegroups[$this->newpagegroup[$i]]; 25275 break; 25276 } 25277 } 25278 } 25279 for ($i = $frompage; $i > $topage; --$i) { 25280 $j = $i - 1; 25281 // shift pages down 25282 $this->setPageBuffer($i, $this->getPageBuffer($j)); 25283 $this->pagedim[$i] = $this->pagedim[$j]; 25284 $this->pagelen[$i] = $this->pagelen[$j]; 25285 $this->intmrk[$i] = $this->intmrk[$j]; 25286 $this->bordermrk[$i] = $this->bordermrk[$j]; 25287 $this->cntmrk[$i] = $this->cntmrk[$j]; 25288 if (isset($this->footerpos[$j])) { 25289 $this->footerpos[$i] = $this->footerpos[$j]; 25290 } elseif (isset($this->footerpos[$i])) { 25291 unset($this->footerpos[$i]); 25292 } 25293 if (isset($this->footerlen[$j])) { 25294 $this->footerlen[$i] = $this->footerlen[$j]; 25295 } elseif (isset($this->footerlen[$i])) { 25296 unset($this->footerlen[$i]); 25297 } 25298 if (isset($this->transfmrk[$j])) { 25299 $this->transfmrk[$i] = $this->transfmrk[$j]; 25300 } elseif (isset($this->transfmrk[$i])) { 25301 unset($this->transfmrk[$i]); 25302 } 25303 if (isset($this->PageAnnots[$j])) { 25304 $this->PageAnnots[$i] = $this->PageAnnots[$j]; 25305 } elseif (isset($this->PageAnnots[$i])) { 25306 unset($this->PageAnnots[$i]); 25307 } 25308 if (isset($this->newpagegroup[$j])) { 25309 $this->newpagegroup[$i] = $this->newpagegroup[$j]; 25310 unset($this->newpagegroup[$j]); 25311 } 25312 if ($this->currpagegroup == $j) { 25313 $this->currpagegroup = $i; 25314 } 25315 } 25316 $this->setPageBuffer($topage, $tmppage); 25317 $this->pagedim[$topage] = $tmppagedim; 25318 $this->pagelen[$topage] = $tmppagelen; 25319 $this->intmrk[$topage] = $tmpintmrk; 25320 $this->bordermrk[$topage] = $tmpbordermrk; 25321 $this->cntmrk[$topage] = $tmpcntmrk; 25322 if (isset($tmpfooterpos)) { 25323 $this->footerpos[$topage] = $tmpfooterpos; 25324 } elseif (isset($this->footerpos[$topage])) { 25325 unset($this->footerpos[$topage]); 25326 } 25327 if (isset($tmpfooterlen)) { 25328 $this->footerlen[$topage] = $tmpfooterlen; 25329 } elseif (isset($this->footerlen[$topage])) { 25330 unset($this->footerlen[$topage]); 25331 } 25332 if (isset($tmptransfmrk)) { 25333 $this->transfmrk[$topage] = $tmptransfmrk; 25334 } elseif (isset($this->transfmrk[$topage])) { 25335 unset($this->transfmrk[$topage]); 25336 } 25337 if (isset($tmpannots)) { 25338 $this->PageAnnots[$topage] = $tmpannots; 25339 } elseif (isset($this->PageAnnots[$topage])) { 25340 unset($this->PageAnnots[$topage]); 25341 } 25342 // adjust outlines 25343 $tmpoutlines = $this->outlines; 25344 foreach ($tmpoutlines as $key => $outline) { 25345 if (($outline['p'] >= $topage) AND ($outline['p'] < $frompage)) { 25346 $this->outlines[$key]['p'] = ($outline['p'] + 1); 25347 } elseif ($outline['p'] == $frompage) { 25348 $this->outlines[$key]['p'] = $topage; 25349 } 25350 } 25351 // adjust dests 25352 $tmpdests = $this->dests; 25353 foreach ($tmpdests as $key => $dest) { 25354 if (($dest['p'] >= $topage) AND ($dest['p'] < $frompage)) { 25355 $this->dests[$key]['p'] = ($dest['p'] + 1); 25356 } elseif ($dest['p'] == $frompage) { 25357 $this->dests[$key]['p'] = $topage; 25358 } 25359 } 25360 // adjust links 25361 $tmplinks = $this->links; 25362 foreach ($tmplinks as $key => $link) { 25363 if (($link[0] >= $topage) AND ($link[0] < $frompage)) { 25364 $this->links[$key][0] = ($link[0] + 1); 25365 } elseif ($link[0] == $frompage) { 25366 $this->links[$key][0] = $topage; 25367 } 25368 } 25369 // adjust javascript 25370 $tmpjavascript = $this->javascript; 25371 global $jfrompage, $jtopage; 25372 $jfrompage = $frompage; 25373 $jtopage = $topage; 25374 $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', 25375 create_function('$matches', 'global $jfrompage, $jtopage; 25376 $pagenum = intval($matches[3]) + 1; 25377 if (($pagenum >= $jtopage) AND ($pagenum < $jfrompage)) { 25378 $newpage = ($pagenum + 1); 25379 } elseif ($pagenum == $jfrompage) { 25380 $newpage = $jtopage; 25381 } else { 25382 $newpage = $pagenum; 25383 } 25384 --$newpage; 25385 return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript); 25386 // return to last page 25387 $this->lastPage(true); 25388 return true; 25389 } 25390 25398 public function deletePage($page) { 25399 if (($page < 1) OR ($page > $this->numpages)) { 25400 return false; 25401 } 25402 // delete current page 25403 unset($this->pages[$page]); 25404 unset($this->pagedim[$page]); 25405 unset($this->pagelen[$page]); 25406 unset($this->intmrk[$page]); 25407 unset($this->bordermrk[$page]); 25408 unset($this->cntmrk[$page]); 25409 if (isset($this->footerpos[$page])) { 25410 unset($this->footerpos[$page]); 25411 } 25412 if (isset($this->footerlen[$page])) { 25413 unset($this->footerlen[$page]); 25414 } 25415 if (isset($this->transfmrk[$page])) { 25416 unset($this->transfmrk[$page]); 25417 } 25418 if (isset($this->PageAnnots[$page])) { 25419 unset($this->PageAnnots[$page]); 25420 } 25421 if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) { 25422 for ($i = $page; $i > 0; --$i) { 25423 if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $page)) { 25424 --$this->pagegroups[$this->newpagegroup[$i]]; 25425 break; 25426 } 25427 } 25428 } 25429 if (isset($this->pageopen[$page])) { 25430 unset($this->pageopen[$page]); 25431 } 25432 if ($page < $this->numpages) { 25433 // update remaining pages 25434 for ($i = $page; $i < $this->numpages; ++$i) { 25435 $j = $i + 1; 25436 // shift pages 25437 $this->setPageBuffer($i, $this->getPageBuffer($j)); 25438 $this->pagedim[$i] = $this->pagedim[$j]; 25439 $this->pagelen[$i] = $this->pagelen[$j]; 25440 $this->intmrk[$i] = $this->intmrk[$j]; 25441 $this->bordermrk[$i] = $this->bordermrk[$j]; 25442 $this->cntmrk[$i] = $this->cntmrk[$j]; 25443 if (isset($this->footerpos[$j])) { 25444 $this->footerpos[$i] = $this->footerpos[$j]; 25445 } elseif (isset($this->footerpos[$i])) { 25446 unset($this->footerpos[$i]); 25447 } 25448 if (isset($this->footerlen[$j])) { 25449 $this->footerlen[$i] = $this->footerlen[$j]; 25450 } elseif (isset($this->footerlen[$i])) { 25451 unset($this->footerlen[$i]); 25452 } 25453 if (isset($this->transfmrk[$j])) { 25454 $this->transfmrk[$i] = $this->transfmrk[$j]; 25455 } elseif (isset($this->transfmrk[$i])) { 25456 unset($this->transfmrk[$i]); 25457 } 25458 if (isset($this->PageAnnots[$j])) { 25459 $this->PageAnnots[$i] = $this->PageAnnots[$j]; 25460 } elseif (isset($this->PageAnnots[$i])) { 25461 unset($this->PageAnnots[$i]); 25462 } 25463 if (isset($this->newpagegroup[$j])) { 25464 $this->newpagegroup[$i] = $this->newpagegroup[$j]; 25465 unset($this->newpagegroup[$j]); 25466 } 25467 if ($this->currpagegroup == $j) { 25468 $this->currpagegroup = $i; 25469 } 25470 if (isset($this->pageopen[$j])) { 25471 $this->pageopen[$i] = $this->pageopen[$j]; 25472 } elseif (isset($this->pageopen[$i])) { 25473 unset($this->pageopen[$i]); 25474 } 25475 } 25476 // remove last page 25477 unset($this->pages[$this->numpages]); 25478 unset($this->pagedim[$this->numpages]); 25479 unset($this->pagelen[$this->numpages]); 25480 unset($this->intmrk[$this->numpages]); 25481 unset($this->bordermrk[$this->numpages]); 25482 unset($this->cntmrk[$this->numpages]); 25483 if (isset($this->footerpos[$this->numpages])) { 25484 unset($this->footerpos[$this->numpages]); 25485 } 25486 if (isset($this->footerlen[$this->numpages])) { 25487 unset($this->footerlen[$this->numpages]); 25488 } 25489 if (isset($this->transfmrk[$this->numpages])) { 25490 unset($this->transfmrk[$this->numpages]); 25491 } 25492 if (isset($this->PageAnnots[$this->numpages])) { 25493 unset($this->PageAnnots[$this->numpages]); 25494 } 25495 if (isset($this->newpagegroup[$this->numpages])) { 25496 unset($this->newpagegroup[$this->numpages]); 25497 } 25498 if ($this->currpagegroup == $this->numpages) { 25499 $this->currpagegroup = ($this->numpages - 1); 25500 } 25501 if (isset($this->pagegroups[$this->numpages])) { 25502 unset($this->pagegroups[$this->numpages]); 25503 } 25504 if (isset($this->pageopen[$this->numpages])) { 25505 unset($this->pageopen[$this->numpages]); 25506 } 25507 } 25508 --$this->numpages; 25509 $this->page = $this->numpages; 25510 // adjust outlines 25511 $tmpoutlines = $this->outlines; 25512 foreach ($tmpoutlines as $key => $outline) { 25513 if ($outline['p'] > $page) { 25514 $this->outlines[$key]['p'] = $outline['p'] - 1; 25515 } elseif ($outline['p'] == $page) { 25516 unset($this->outlines[$key]); 25517 } 25518 } 25519 // adjust dests 25520 $tmpdests = $this->dests; 25521 foreach ($tmpdests as $key => $dest) { 25522 if ($dest['p'] > $page) { 25523 $this->dests[$key]['p'] = $dest['p'] - 1; 25524 } elseif ($dest['p'] == $page) { 25525 unset($this->dests[$key]); 25526 } 25527 } 25528 // adjust links 25529 $tmplinks = $this->links; 25530 foreach ($tmplinks as $key => $link) { 25531 if ($link[0] > $page) { 25532 $this->links[$key][0] = $link[0] - 1; 25533 } elseif ($link[0] == $page) { 25534 unset($this->links[$key]); 25535 } 25536 } 25537 // adjust javascript 25538 $tmpjavascript = $this->javascript; 25539 global $jpage; 25540 $jpage = $page; 25541 $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', 25542 create_function('$matches', 'global $jpage; 25543 $pagenum = intval($matches[3]) + 1; 25544 if ($pagenum >= $jpage) { 25545 $newpage = ($pagenum - 1); 25546 } elseif ($pagenum == $jpage) { 25547 $newpage = 1; 25548 } else { 25549 $newpage = $pagenum; 25550 } 25551 --$newpage; 25552 return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript); 25553 // return to last page 25554 $this->lastPage(true); 25555 return true; 25556 } 25557 25565 public function copyPage($page=0) { 25566 if ($page == 0) { 25567 // default value 25568 $page = $this->page; 25569 } 25570 if (($page < 1) OR ($page > $this->numpages)) { 25571 return false; 25572 } 25573 // close the last page 25574 $this->endPage(); 25575 // copy all page-related states 25576 ++$this->numpages; 25577 $this->page = $this->numpages; 25578 $this->setPageBuffer($this->page, $this->getPageBuffer($page)); 25579 $this->pagedim[$this->page] = $this->pagedim[$page]; 25580 $this->pagelen[$this->page] = $this->pagelen[$page]; 25581 $this->intmrk[$this->page] = $this->intmrk[$page]; 25582 $this->bordermrk[$this->page] = $this->bordermrk[$page]; 25583 $this->cntmrk[$this->page] = $this->cntmrk[$page]; 25584 $this->pageopen[$this->page] = false; 25585 if (isset($this->footerpos[$page])) { 25586 $this->footerpos[$this->page] = $this->footerpos[$page]; 25587 } 25588 if (isset($this->footerlen[$page])) { 25589 $this->footerlen[$this->page] = $this->footerlen[$page]; 25590 } 25591 if (isset($this->transfmrk[$page])) { 25592 $this->transfmrk[$this->page] = $this->transfmrk[$page]; 25593 } 25594 if (isset($this->PageAnnots[$page])) { 25595 $this->PageAnnots[$this->page] = $this->PageAnnots[$page]; 25596 } 25597 if (isset($this->newpagegroup[$page])) { 25598 // start a new group 25599 $this->newpagegroup[$this->page] = sizeof($this->newpagegroup) + 1; 25600 $this->currpagegroup = $this->newpagegroup[$this->page]; 25601 $this->pagegroups[$this->currpagegroup] = 1; 25602 } elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) { 25603 ++$this->pagegroups[$this->currpagegroup]; 25604 } 25605 // copy outlines 25606 $tmpoutlines = $this->outlines; 25607 foreach ($tmpoutlines as $key => $outline) { 25608 if ($outline['p'] == $page) { 25609 $this->outlines[] = array('t' => $outline['t'], 'l' => $outline['l'], 'y' => $outline['y'], 'p' => $this->page, 's' => $outline['s'], 'c' => $outline['c']); 25610 } 25611 } 25612 // copy links 25613 $tmplinks = $this->links; 25614 foreach ($tmplinks as $key => $link) { 25615 if ($link[0] == $page) { 25616 $this->links[] = array($this->page, $link[1]); 25617 } 25618 } 25619 // return to last page 25620 $this->lastPage(true); 25621 return true; 25622 } 25623 25641 public function addTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC', $style='', $color=array(0,0,0)) { 25642 $fontsize = $this->FontSizePt; 25643 $fontfamily = $this->FontFamily; 25644 $fontstyle = $this->FontStyle; 25645 $w = $this->w - $this->lMargin - $this->rMargin; 25646 $spacer = $this->GetStringWidth(chr(32)) * 4; 25647 $page_first = $this->getPage(); 25648 $lmargin = $this->lMargin; 25649 $rmargin = $this->rMargin; 25650 $x_start = $this->GetX(); 25651 $current_page = $this->page; 25652 $current_column = $this->current_column; 25653 if ($this->empty_string($numbersfont)) { 25654 $numbersfont = $this->default_monospaced_font; 25655 } 25656 if ($this->empty_string($filler)) { 25657 $filler = ' '; 25658 } 25659 if ($this->empty_string($page)) { 25660 $gap = ' '; 25661 } else { 25662 $gap = ''; 25663 if ($page < 1) { 25664 $page = 1; 25665 } 25666 } 25667 $this->SetFont($numbersfont, $fontstyle, $fontsize); 25668 $numwidth = $this->GetStringWidth('00000'); 25669 $maxpage = 0; //used for pages on attached documents 25670 foreach ($this->outlines as $key => $outline) { 25671 // check for extra pages (used for attachments) 25672 if (($this->page > $page_first) AND ($outline['p'] >= $this->numpages)) { 25673 $outline['p'] += ($this->page - $page_first); 25674 } 25675 if ($this->rtl) { 25676 $aligntext = 'R'; 25677 $alignnum = 'L'; 25678 } else { 25679 $aligntext = 'L'; 25680 $alignnum = 'R'; 25681 } 25682 if ($outline['l'] == 0) { 25683 $this->SetFont($fontfamily, $fontstyle.'B', $fontsize); 25684 } else { 25685 $this->SetFont($fontfamily, $fontstyle, $fontsize - $outline['l']); 25686 } 25687 // check for page break 25688 $this->checkPageBreak((2 * $this->FontSize * $this->cell_height_ratio)); 25689 // set margins and X position 25690 if (($this->page == $current_page) AND ($this->current_column == $current_column)) { 25691 $this->lMargin = $lmargin; 25692 $this->rMargin = $rmargin; 25693 } else { 25694 if ($this->current_column != $current_column) { 25695 if ($this->rtl) { 25696 $x_start = $this->w - $this->columns[$this->current_column]['x']; 25697 } else { 25698 $x_start = $this->columns[$this->current_column]['x']; 25699 } 25700 } 25701 $lmargin = $this->lMargin; 25702 $rmargin = $this->rMargin; 25703 $current_page = $this->page; 25704 $current_column = $this->current_column; 25705 } 25706 $this->SetX($x_start); 25707 $indent = ($spacer * $outline['l']); 25708 if ($this->rtl) { 25709 $this->x -= $indent; 25710 $this->rMargin = $this->w - $this->x; 25711 } else { 25712 $this->x += $indent; 25713 $this->lMargin = $this->x; 25714 } 25715 $link = $this->AddLink(); 25716 $this->SetLink($link, $outline['y'], $outline['p']); 25717 // write the text 25718 if ($this->rtl) { 25719 $txt = ' '.$outline['t']; 25720 } else { 25721 $txt = $outline['t'].' '; 25722 } 25723 $this->Write(0, $txt, $link, false, $aligntext, false, 0, false, false, 0, $numwidth, ''); 25724 if ($this->rtl) { 25725 $tw = $this->x - $this->lMargin; 25726 } else { 25727 $tw = $this->w - $this->rMargin - $this->x; 25728 } 25729 $this->SetFont($numbersfont, $fontstyle, $fontsize); 25730 if ($this->empty_string($page)) { 25731 $pagenum = $outline['p']; 25732 } else { 25733 // placemark to be replaced with the correct number 25734 $pagenum = '{#'.($outline['p']).'}'; 25735 if ($this->isUnicodeFont()) { 25736 $pagenum = '{'.$pagenum.'}'; 25737 } 25738 $maxpage = max($maxpage, $outline['p']); 25739 } 25740 $fw = ($tw - $this->GetStringWidth($pagenum.$filler)); 25741 $numfills = floor($fw / $this->GetStringWidth($filler)); 25742 if ($numfills > 0) { 25743 $rowfill = str_repeat($filler, $numfills); 25744 } else { 25745 $rowfill = ''; 25746 } 25747 if ($this->rtl) { 25748 $pagenum = $pagenum.$gap.$rowfill; 25749 } else { 25750 $pagenum = $rowfill.$gap.$pagenum; 25751 } 25752 // write the number 25753 $this->Cell($tw, 0, $pagenum, 0, 1, $alignnum, 0, $link, 0); 25754 } 25755 $page_last = $this->getPage(); 25756 $maxpage = max($maxpage, $page_last); 25757 $numpages = $page_last - $page_first + 1; 25758 if (!$this->empty_string($page)) { 25759 for ($p = $page_first; $p <= $page_last; ++$p) { 25760 // get page data 25761 $temppage = $this->getPageBuffer($p); 25762 for ($n = 1; $n <= $maxpage; ++$n) { 25763 // update page numbers 25764 $a = '{#'.$n.'}'; 25765 // get page number aliases 25766 $pnalias = $this->getInternalPageNumberAliases($a); 25767 // calculate replacement number 25768 if (($n >= $page) AND ($n <= $this->numpages)) { 25769 $np = $n + $numpages; 25770 } else { 25771 $np = $n; 25772 } 25773 $na = $this->formatTOCPageNumber(($this->starting_page_number + $np - 1)); 25774 $nu = $this->UTF8ToUTF16BE($na, false); 25775 // replace aliases with numbers 25776 foreach ($pnalias['u'] as $u) { 25777 $sfill = str_repeat($filler, max(0, (strlen($u) - strlen($nu.' ')))); 25778 if ($this->rtl) { 25779 $nr = $nu.$this->UTF8ToUTF16BE(' '.$sfill); 25780 } else { 25781 $nr = $this->UTF8ToUTF16BE($sfill.' ').$nu; 25782 } 25783 $temppage = str_replace($u, $nr, $temppage); 25784 } 25785 foreach ($pnalias['a'] as $a) { 25786 $sfill = str_repeat($filler, max(0, (strlen($a) - strlen($na.' ')))); 25787 if ($this->rtl) { 25788 $nr = $na.' '.$sfill; 25789 } else { 25790 $nr = $sfill.' '.$na; 25791 } 25792 $temppage = str_replace($a, $nr, $temppage); 25793 } 25794 } 25795 // save changes 25796 $this->setPageBuffer($p, $temppage); 25797 } 25798 // move pages 25799 $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color); 25800 for ($i = 0; $i < $numpages; ++$i) { 25801 $this->movePage($page_last, $page); 25802 } 25803 } 25804 } 25805 25822 public function addHTMLTOC($page='', $toc_name='TOC', $templates=array(), $correct_align=true, $style='', $color=array(0,0,0)) { 25823 $filler = ' '; 25824 $prev_htmlLinkColorArray = $this->htmlLinkColorArray; 25825 $prev_htmlLinkFontStyle = $this->htmlLinkFontStyle; 25826 // set new style for link 25827 $this->htmlLinkColorArray = array(); 25828 $this->htmlLinkFontStyle = ''; 25829 $page_first = $this->getPage(); 25830 // get the font type used for numbers in each template 25831 $current_font = $this->FontFamily; 25832 foreach ($templates as $level => $html) { 25833 $dom = $this->getHtmlDomArray($html); 25834 foreach ($dom as $key => $value) { 25835 if ($value['value'] == '#TOC_PAGE_NUMBER#') { 25836 $this->SetFont($dom[($key - 1)]['fontname']); 25837 $templates['F'.$level] = $this->isUnicodeFont(); 25838 } 25839 } 25840 } 25841 $this->SetFont($current_font); 25842 $maxpage = 0; //used for pages on attached documents 25843 foreach ($this->outlines as $key => $outline) { 25844 // get HTML template 25845 $row = $templates[$outline['l']]; 25846 if ($this->empty_string($page)) { 25847 $pagenum = $outline['p']; 25848 } else { 25849 // placemark to be replaced with the correct number 25850 $pagenum = '{#'.($outline['p']).'}'; 25851 if ($templates['F'.$outline['l']]) { 25852 $pagenum = '{'.$pagenum.'}'; 25853 } 25854 $maxpage = max($maxpage, $outline['p']); 25855 } 25856 // replace templates with current values 25857 $row = str_replace('#TOC_DESCRIPTION#', $outline['t'], $row); 25858 $row = str_replace('#TOC_PAGE_NUMBER#', $pagenum, $row); 25859 // add link to page 25860 $row = '<a href="#'.$outline['p'].','.$outline['y'].'">'.$row.'</a>'; 25861 // write bookmark entry 25862 $this->writeHTML($row, false, false, true, false, ''); 25863 } 25864 // restore link styles 25865 $this->htmlLinkColorArray = $prev_htmlLinkColorArray; 25866 $this->htmlLinkFontStyle = $prev_htmlLinkFontStyle; 25867 // move TOC page and replace numbers 25868 $page_last = $this->getPage(); 25869 $maxpage = max($maxpage, $page_last); 25870 $numpages = $page_last - $page_first + 1; 25871 if (!$this->empty_string($page)) { 25872 for ($p = $page_first; $p <= $page_last; ++$p) { 25873 // get page data 25874 $temppage = $this->getPageBuffer($p); 25875 for ($n = 1; $n <= $maxpage; ++$n) { 25876 // update page numbers 25877 $a = '{#'.$n.'}'; 25878 // get page number aliases 25879 $pnalias = $this->getInternalPageNumberAliases($a); 25880 // calculate replacement number 25881 if ($n >= $page) { 25882 $np = $n + $numpages; 25883 } else { 25884 $np = $n; 25885 } 25886 $na = $this->formatTOCPageNumber(($this->starting_page_number + $np - 1)); 25887 $nu = $this->UTF8ToUTF16BE($na, false); 25888 // replace aliases with numbers 25889 foreach ($pnalias['u'] as $u) { 25890 if ($correct_align) { 25891 $sfill = str_repeat($filler, (strlen($u) - strlen($nu.' '))); 25892 if ($this->rtl) { 25893 $nr = $nu.$this->UTF8ToUTF16BE(' '.$sfill); 25894 } else { 25895 $nr = $this->UTF8ToUTF16BE($sfill.' ').$nu; 25896 } 25897 } else { 25898 $nr = $nu; 25899 } 25900 $temppage = str_replace($u, $nr, $temppage); 25901 } 25902 foreach ($pnalias['a'] as $a) { 25903 if ($correct_align) { 25904 $sfill = str_repeat($filler, (strlen($a) - strlen($na.' '))); 25905 if ($this->rtl) { 25906 $nr = $na.' '.$sfill; 25907 } else { 25908 $nr = $sfill.' '.$na; 25909 } 25910 } else { 25911 $nr = $na; 25912 } 25913 $temppage = str_replace($a, $nr, $temppage); 25914 } 25915 } 25916 // save changes 25917 $this->setPageBuffer($p, $temppage); 25918 } 25919 // move pages 25920 $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color); 25921 for ($i = 0; $i < $numpages; ++$i) { 25922 $this->movePage($page_last, $page); 25923 } 25924 } 25925 } 25926 25932 public function startTransaction() { 25933 if (isset($this->objcopy)) { 25934 // remove previous copy 25935 $this->commitTransaction(); 25936 } 25937 // record current page number and Y position 25938 $this->start_transaction_page = $this->page; 25939 $this->start_transaction_y = $this->y; 25940 // clone current object 25941 $this->objcopy = $this->objclone($this); 25942 } 25943 25949 public function commitTransaction() { 25950 if (isset($this->objcopy)) { 25951 $this->objcopy->_destroy(true, true); 25952 unset($this->objcopy); 25953 } 25954 } 25955 25963 public function rollbackTransaction($self=false) { 25964 if (isset($this->objcopy)) { 25965 if (isset($this->objcopy->diskcache) AND $this->objcopy->diskcache) { 25966 // truncate files to previous values 25967 foreach ($this->objcopy->cache_file_length as $file => $length) { 25968 $file = substr($file, 1); 25969 $handle = fopen($file, 'r+'); 25970 ftruncate($handle, $length); 25971 } 25972 } 25973 $this->_destroy(true, true); 25974 if ($self) { 25975 $objvars = get_object_vars($this->objcopy); 25976 foreach ($objvars as $key => $value) { 25977 $this->$key = $value; 25978 } 25979 } 25980 return $this->objcopy; 25981 } 25982 return $this; 25983 } 25984 25992 public function objclone($object) { 25993 return @clone($object); 25994 } 25995 26003 public function empty_string($str) { 26004 return (is_null($str) OR (is_string($str) AND (strlen($str) == 0))); 26005 } 26006 26016 public function revstrpos($haystack, $needle, $offset = 0) { 26017 $length = strlen($haystack); 26018 $offset = ($offset > 0)?($length - $offset):abs($offset); 26019 $pos = strpos(strrev($haystack), strrev($needle), $offset); 26020 return ($pos === false)?false:($length - $pos - strlen($needle)); 26021 } 26022 26023 // --- MULTI COLUMNS METHODS ----------------------- 26024 26033 public function setEqualColumns($numcols=0, $width=0, $y='') { 26034 $this->columns = array(); 26035 if ($numcols < 2) { 26036 $numcols = 0; 26037 $this->columns = array(); 26038 } else { 26039 // maximum column width 26040 $maxwidth = ($this->w - $this->original_lMargin - $this->original_rMargin) / $numcols; 26041 if (($width == 0) OR ($width > $maxwidth)) { 26042 $width = $maxwidth; 26043 } 26044 if ($this->empty_string($y)) { 26045 $y = $this->y; 26046 } 26047 // space between columns 26048 $space = (($this->w - $this->original_lMargin - $this->original_rMargin - ($numcols * $width)) / ($numcols - 1)); 26049 // fill the columns array (with, space, starting Y position) 26050 for ($i = 0; $i < $numcols; ++$i) { 26051 $this->columns[$i] = array('w' => $width, 's' => $space, 'y' => $y); 26052 } 26053 } 26054 $this->num_columns = $numcols; 26055 $this->current_column = 0; 26056 $this->column_start_page = $this->page; 26057 $this->selectColumn(0); 26058 } 26059 26065 public function resetColumns() { 26066 $this->lMargin = $this->original_lMargin; 26067 $this->rMargin = $this->original_rMargin; 26068 $this->setEqualColumns(); 26069 } 26070 26078 public function setColumnsArray($columns) { 26079 $this->columns = $columns; 26080 $this->num_columns = count($columns); 26081 $this->current_column = 0; 26082 $this->column_start_page = $this->page; 26083 $this->selectColumn(0); 26084 } 26085 26092 public function selectColumn($col='') { 26093 if (is_string($col)) { 26094 $col = $this->current_column; 26095 } elseif ($col >= $this->num_columns) { 26096 $col = 0; 26097 } 26098 $xshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0)); 26099 $enable_thead = false; 26100 if ($this->num_columns > 1) { 26101 if ($col != $this->current_column) { 26102 // move Y pointer at the top of the column 26103 if ($this->column_start_page == $this->page) { 26104 $this->y = $this->columns[$col]['y']; 26105 } else { 26106 $this->y = $this->tMargin; 26107 } 26108 // Avoid to write table headers more than once 26109 if (($this->page > $this->maxselcol['page']) OR (($this->page == $this->maxselcol['page']) AND ($col > $this->maxselcol['column']))) { 26110 $enable_thead = true; 26111 $this->maxselcol['page'] = $this->page; 26112 $this->maxselcol['column'] = $col; 26113 } 26114 } 26115 $xshift = $this->colxshift; 26116 // set X position of the current column by case 26117 $listindent = ($this->listindentlevel * $this->listindent); 26118 // calculate column X position 26119 $colpos = 0; 26120 for ($i = 0; $i < $col; ++$i) { 26121 $colpos += ($this->columns[$i]['w'] + $this->columns[$i]['s']); 26122 } 26123 if ($this->rtl) { 26124 $x = $this->w - $this->original_rMargin - $colpos; 26125 $this->rMargin = ($this->w - $x + $listindent); 26126 $this->lMargin = ($x - $this->columns[$col]['w']); 26127 $this->x = $x - $listindent; 26128 } else { 26129 $x = $this->original_lMargin + $colpos; 26130 $this->lMargin = ($x + $listindent); 26131 $this->rMargin = ($this->w - $x - $this->columns[$col]['w']); 26132 $this->x = $x + $listindent; 26133 } 26134 $this->columns[$col]['x'] = $x; 26135 } 26136 $this->current_column = $col; 26137 // fix for HTML mode 26138 $this->newline = true; 26139 // print HTML table header (if any) 26140 if ((!$this->empty_string($this->thead)) AND (!$this->inthead)) { 26141 if ($enable_thead) { 26142 // print table header 26143 $this->writeHTML($this->thead, false, false, false, false, ''); 26144 $this->y += $xshift['s']['V']; 26145 // store end of header position 26146 if (!isset($this->columns[$col]['th'])) { 26147 $this->columns[$col]['th'] = array(); 26148 } 26149 $this->columns[$col]['th']['\''.$this->page.'\''] = $this->y; 26150 $this->lasth = 0; 26151 } elseif (isset($this->columns[$col]['th']['\''.$this->page.'\''])) { 26152 $this->y = $this->columns[$col]['th']['\''.$this->page.'\'']; 26153 } 26154 } 26155 // account for an html table cell over multiple columns 26156 if ($this->rtl) { 26157 $this->rMargin += $xshift['x']; 26158 $this->x -= ($xshift['x'] + $xshift['p']['R']); 26159 } else { 26160 $this->lMargin += $xshift['x']; 26161 $this->x += $xshift['x'] + $xshift['p']['L']; 26162 } 26163 } 26164 26171 public function getColumn() { 26172 return $this->current_column; 26173 } 26174 26181 public function getNumberOfColumns() { 26182 return $this->num_columns; 26183 } 26184 26192 public function serializeTCPDFtagParameters($pararray) { 26193 return urlencode(serialize($pararray)); 26194 } 26195 26204 public function setTextRenderingMode($stroke=0, $fill=true, $clip=false) { 26205 // Ref.: PDF 32000-1:2008 - 9.3.6 Text Rendering Mode 26206 // convert text rendering parameters 26207 if ($stroke < 0) { 26208 $stroke = 0; 26209 } 26210 if ($fill === true) { 26211 if ($stroke > 0) { 26212 if ($clip === true) { 26213 // Fill, then stroke text and add to path for clipping 26214 $textrendermode = 6; 26215 } else { 26216 // Fill, then stroke text 26217 $textrendermode = 2; 26218 } 26219 $textstrokewidth = $stroke; 26220 } else { 26221 if ($clip === true) { 26222 // Fill text and add to path for clipping 26223 $textrendermode = 4; 26224 } else { 26225 // Fill text 26226 $textrendermode = 0; 26227 } 26228 } 26229 } else { 26230 if ($stroke > 0) { 26231 if ($clip === true) { 26232 // Stroke text and add to path for clipping 26233 $textrendermode = 5; 26234 } else { 26235 // Stroke text 26236 $textrendermode = 1; 26237 } 26238 $textstrokewidth = $stroke; 26239 } else { 26240 if ($clip === true) { 26241 // Add text to path for clipping 26242 $textrendermode = 7; 26243 } else { 26244 // Neither fill nor stroke text (invisible) 26245 $textrendermode = 3; 26246 } 26247 } 26248 } 26249 $this->textrendermode = $textrendermode; 26250 $this->textstrokewidth = $stroke * $this->k; 26251 } 26252 26267 protected function hyphenateWord($word, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) { 26268 $hyphenword = array(); // hyphens positions 26269 $numchars = count($word); 26270 if ($numchars <= $charmin) { 26271 return $word; 26272 } 26273 $word_string = $this->UTF8ArrSubString($word); 26274 // some words will be returned as-is 26275 $pattern = '/^([a-zA-Z0-9_\.\-]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/'; 26276 if (preg_match($pattern, $word_string) > 0) { 26277 // email 26278 return $word; 26279 } 26280 $pattern = '/(([a-zA-Z0-9\-]+\.)?)((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/'; 26281 if (preg_match($pattern, $word_string) > 0) { 26282 // URL 26283 return $word; 26284 } 26285 if (isset($dictionary[$word_string])) { 26286 return $this->UTF8StringToArray($dictionary[$word_string]); 26287 } 26288 // suround word with '_' characters 26289 $tmpword = array_merge(array(95), $word, array(95)); 26290 $tmpnumchars = $numchars + 2; 26291 $maxpos = $tmpnumchars - $charmin; 26292 for ($pos = 0; $pos < $maxpos; ++$pos) { 26293 $imax = min(($tmpnumchars - $pos), $charmax); 26294 for ($i = $charmin; $i <= $imax; ++$i) { 26295 $subword = strtolower($this->UTF8ArrSubString($tmpword, $pos, $pos + $i)); 26296 if (isset($patterns[$subword])) { 26297 $pattern = $this->UTF8StringToArray($patterns[$subword]); 26298 $pattern_length = count($pattern); 26299 $digits = 1; 26300 for ($j = 0; $j < $pattern_length; ++$j) { 26301 // check if $pattern[$j] is a number 26302 if (($pattern[$j] >= 48) AND ($pattern[$j] <= 57)) { 26303 if ($j == 0) { 26304 $zero = $pos - 1; 26305 } else { 26306 $zero = $pos + $j - $digits; 26307 } 26308 if (!isset($hyphenword[$zero]) OR ($hyphenword[$zero] != $pattern[$j])) { 26309 $hyphenword[$zero] = $this->unichr($pattern[$j]); 26310 } 26311 ++$digits; 26312 } 26313 } 26314 } 26315 } 26316 } 26317 $inserted = 0; 26318 $maxpos = $numchars - $rightmin; 26319 for ($i = $leftmin; $i <= $maxpos; ++$i) { 26320 if (isset($hyphenword[$i]) AND (($hyphenword[$i] % 2) != 0)) { 26321 // 173 = soft hyphen character 26322 array_splice($word, $i + $inserted, 0, 173); 26323 ++$inserted; 26324 } 26325 } 26326 return $word; 26327 } 26328 26337 public function getHyphenPatternsFromTEX($file) { 26338 // TEX patterns are available at: 26339 // http://www.ctan.org/tex-archive/language/hyph-utf8/tex/generic/hyph-utf8/patterns/ 26340 $data = file_get_contents($file); 26341 $patterns = array(); 26342 // remove comments 26343 $data = preg_replace('/\%[^\n]*/', '', $data); 26344 // extract the patterns part 26345 preg_match('/\\\\patterns\{([^\}]*)\}/i', $data, $matches); 26346 $data = trim(substr($matches[0], 10, -1)); 26347 // extract each pattern 26348 $patterns_array = preg_split('/[\s]+/', $data); 26349 // create new language array of patterns 26350 $patterns = array(); 26351 foreach($patterns_array as $val) { 26352 if (!$this->empty_string($val)) { 26353 $val = trim($val); 26354 $val = str_replace('\'', '\\\'', $val); 26355 $key = preg_replace('/[0-9]+/', '', $val); 26356 $patterns[$key] = $val; 26357 } 26358 } 26359 return $patterns; 26360 } 26361 26376 public function hyphenateText($text, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) { 26377 $text = $this->unhtmlentities($text); 26378 $word = array(); // last word 26379 $txtarr = array(); // text to be returned 26380 $intag = false; // true if we are inside an HTML tag 26381 if (!is_array($patterns)) { 26382 $patterns = $this->getHyphenPatternsFromTEX($patterns); 26383 } 26384 // get array of characters 26385 $unichars = $this->UTF8StringToArray($text); 26386 // for each char 26387 foreach ($unichars as $char) { 26388 if ((!$intag) AND $this->unicode->uni_type[$char] == 'L') { 26389 // letter character 26390 $word[] = $char; 26391 } else { 26392 // other type of character 26393 if (!$this->empty_string($word)) { 26394 // hypenate the word 26395 $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax)); 26396 $word = array(); 26397 } 26398 $txtarr[] = $char; 26399 if (chr($char) == '<') { 26400 // we are inside an HTML tag 26401 $intag = true; 26402 } elseif ($intag AND (chr($char) == '>')) { 26403 // end of HTML tag 26404 $intag = false; 26405 } 26406 } 26407 } 26408 if (!$this->empty_string($word)) { 26409 // hypenate the word 26410 $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax)); 26411 } 26412 // convert char array to string and return 26413 return $this->UTF8ArrSubString($txtarr); 26414 } 26415 26422 public function setRasterizeVectorImages($mode) { 26423 $this->rasterize_vector_images = $mode; 26424 } 26425 26447 protected function getPathPaintOperator($style, $default='S') { 26448 $op = ''; 26449 switch($style) { 26450 case 'S': 26451 case 'D': { 26452 $op = 'S'; 26453 break; 26454 } 26455 case 's': 26456 case 'd': { 26457 $op = 's'; 26458 break; 26459 } 26460 case 'f': 26461 case 'F': { 26462 $op = 'f'; 26463 break; 26464 } 26465 case 'f*': 26466 case 'F*': { 26467 $op = 'f*'; 26468 break; 26469 } 26470 case 'B': 26471 case 'FD': 26472 case 'DF': { 26473 $op = 'B'; 26474 break; 26475 } 26476 case 'B*': 26477 case 'F*D': 26478 case 'DF*': { 26479 $op = 'B*'; 26480 break; 26481 } 26482 case 'b': 26483 case 'fd': 26484 case 'df': { 26485 $op = 'b'; 26486 break; 26487 } 26488 case 'b*': 26489 case 'f*d': 26490 case 'df*': { 26491 $op = 'b*'; 26492 break; 26493 } 26494 case 'CNZ': { 26495 $op = 'W n'; 26496 break; 26497 } 26498 case 'CEO': { 26499 $op = 'W* n'; 26500 break; 26501 } 26502 case 'n': { 26503 $op = 'n'; 26504 break; 26505 } 26506 default: { 26507 if (!empty($default)) { 26508 $op = $this->getPathPaintOperator($default, ''); 26509 } else { 26510 $op = ''; 26511 } 26512 } 26513 } 26514 return $op; 26515 } 26516 26524 public function setFontSubsetting($enable=true) { 26525 if ($this->pdfa_mode) { 26526 $this->font_subsetting = false; 26527 } else { 26528 $this->font_subsetting = $enable ? true : false; 26529 } 26530 } 26531 26539 public function getFontSubsetting() { 26540 return $this->font_subsetting; 26541 } 26542 26552 public function stringLeftTrim($str, $replace='') { 26553 return preg_replace('/^'.$this->re_space['p'].'+/'.$this->re_space['m'], $replace, $str); 26554 } 26555 26565 public function stringRightTrim($str, $replace='') { 26566 return preg_replace('/'.$this->re_space['p'].'+$/'.$this->re_space['m'], $replace, $str); 26567 } 26568 26578 public function stringTrim($str, $replace='') { 26579 $str = $this->stringLeftTrim($str, $replace); 26580 $str = $this->stringRightTrim($str, $replace); 26581 return $str; 26582 } 26583 26591 public function isUnicodeFont() { 26592 return (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')); 26593 } 26594 26603 public function getFontFamilyName($fontfamily) { 26604 // remove spaces and symbols 26605 $fontfamily = preg_replace('/[^a-z0-9\,]/', '', strtolower($fontfamily)); 26606 // extract all font names 26607 $fontslist = preg_split('/[,]/', $fontfamily); 26608 // find first valid font name 26609 foreach ($fontslist as $font) { 26610 // replace font variations 26611 $font = preg_replace('/italic$/', 'I', $font); 26612 $font = preg_replace('/oblique$/', 'I', $font); 26613 $font = preg_replace('/bold([I]?)$/', 'B\\1', $font); 26614 // replace common family names and core fonts 26615 $pattern = array(); 26616 $replacement = array(); 26617 $pattern[] = '/^serif|^cursive|^fantasy|^timesnewroman/'; 26618 $replacement[] = 'times'; 26619 $pattern[] = '/^sansserif/'; 26620 $replacement[] = 'helvetica'; 26621 $pattern[] = '/^monospace/'; 26622 $replacement[] = 'courier'; 26623 $font = preg_replace($pattern, $replacement, $font); 26624 if (in_array(strtolower($font), $this->fontlist) OR in_array($font, $this->fontkeys)) { 26625 return $font; 26626 } 26627 } 26628 // return current font as default 26629 return $this->CurrentFont['fontkey']; 26630 } 26631 26646 public function startTemplate($w=0, $h=0, $group=false) { 26647 if ($this->inxobj) { 26648 // we are already inside an XObject template 26649 return false; 26650 } 26651 $this->inxobj = true; 26652 ++$this->n; 26653 // XObject ID 26654 $this->xobjid = 'XT'.$this->n; 26655 // object ID 26656 $this->xobjects[$this->xobjid] = array('n' => $this->n); 26657 // store current graphic state 26658 $this->xobjects[$this->xobjid]['gvars'] = $this->getGraphicVars(); 26659 // initialize data 26660 $this->xobjects[$this->xobjid]['intmrk'] = 0; 26661 $this->xobjects[$this->xobjid]['transfmrk'] = array(); 26662 $this->xobjects[$this->xobjid]['outdata'] = ''; 26663 $this->xobjects[$this->xobjid]['xobjects'] = array(); 26664 $this->xobjects[$this->xobjid]['images'] = array(); 26665 $this->xobjects[$this->xobjid]['fonts'] = array(); 26666 $this->xobjects[$this->xobjid]['annotations'] = array(); 26667 $this->xobjects[$this->xobjid]['extgstates'] = array(); 26668 $this->xobjects[$this->xobjid]['gradients'] = array(); 26669 $this->xobjects[$this->xobjid]['spot_colors'] = array(); 26670 // set new environment 26671 $this->num_columns = 1; 26672 $this->current_column = 0; 26673 $this->SetAutoPageBreak(false); 26674 if (($w === '') OR ($w <= 0)) { 26675 $w = $this->w - $this->lMargin - $this->rMargin; 26676 } 26677 if (($h === '') OR ($h <= 0)) { 26678 $h = $this->h - $this->tMargin - $this->bMargin; 26679 } 26680 $this->xobjects[$this->xobjid]['x'] = 0; 26681 $this->xobjects[$this->xobjid]['y'] = 0; 26682 $this->xobjects[$this->xobjid]['w'] = $w; 26683 $this->xobjects[$this->xobjid]['h'] = $h; 26684 $this->w = $w; 26685 $this->h = $h; 26686 $this->wPt = $this->w * $this->k; 26687 $this->hPt = $this->h * $this->k; 26688 $this->fwPt = $this->wPt; 26689 $this->fhPt = $this->hPt; 26690 $this->x = 0; 26691 $this->y = 0; 26692 $this->lMargin = 0; 26693 $this->rMargin = 0; 26694 $this->tMargin = 0; 26695 $this->bMargin = 0; 26696 // set group mode 26697 $this->xobjects[$this->xobjid]['group'] = $group; 26698 return $this->xobjid; 26699 } 26700 26711 public function endTemplate() { 26712 if (!$this->inxobj) { 26713 // we are not inside a template 26714 return false; 26715 } 26716 $this->inxobj = false; 26717 // restore previous graphic state 26718 $this->setGraphicVars($this->xobjects[$this->xobjid]['gvars'], true); 26719 return $this->xobjid; 26720 } 26721 26740 public function printTemplate($id, $x='', $y='', $w=0, $h=0, $align='', $palign='', $fitonpage=false) { 26741 if (!isset($this->xobjects[$id])) { 26742 $this->Error('The XObject Template \''.$id.'\' doesn\'t exist!'); 26743 } 26744 if ($this->inxobj) { 26745 if ($id == $this->xobjid) { 26746 // close current template 26747 $this->endTemplate(); 26748 } else { 26749 // use the template as resource for the template currently opened 26750 $this->xobjects[$this->xobjid]['xobjects'][$id] = $this->xobjects[$id]; 26751 } 26752 } 26753 // set default values 26754 if ($x === '') { 26755 $x = $this->x; 26756 } 26757 if ($y === '') { 26758 $y = $this->y; 26759 } 26760 // check page for no-write regions and adapt page margins if necessary 26761 list($x, $y) = $this->checkPageRegions($h, $x, $y); 26762 $ow = $this->xobjects[$id]['w']; 26763 $oh = $this->xobjects[$id]['h']; 26764 // calculate template width and height on document 26765 if (($w <= 0) AND ($h <= 0)) { 26766 $w = $ow; 26767 $h = $oh; 26768 } elseif ($w <= 0) { 26769 $w = $h * $ow / $oh; 26770 } elseif ($h <= 0) { 26771 $h = $w * $oh / $ow; 26772 } 26773 // fit the template on available space 26774 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage); 26775 // set page alignment 26776 $rb_y = $y + $h; 26777 // set alignment 26778 if ($this->rtl) { 26779 if ($palign == 'L') { 26780 $xt = $this->lMargin; 26781 } elseif ($palign == 'C') { 26782 $xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 26783 } elseif ($palign == 'R') { 26784 $xt = $this->w - $this->rMargin - $w; 26785 } else { 26786 $xt = $x - $w; 26787 } 26788 $rb_x = $xt; 26789 } else { 26790 if ($palign == 'L') { 26791 $xt = $this->lMargin; 26792 } elseif ($palign == 'C') { 26793 $xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 26794 } elseif ($palign == 'R') { 26795 $xt = $this->w - $this->rMargin - $w; 26796 } else { 26797 $xt = $x; 26798 } 26799 $rb_x = $xt + $w; 26800 } 26801 // print XObject Template + Transformation matrix 26802 $this->StartTransform(); 26803 // translate and scale 26804 $sx = ($w / $this->xobjects[$id]['w']); 26805 $sy = ($h / $this->xobjects[$id]['h']); 26806 $tm = array(); 26807 $tm[0] = $sx; 26808 $tm[1] = 0; 26809 $tm[2] = 0; 26810 $tm[3] = $sy; 26811 $tm[4] = $xt * $this->k; 26812 $tm[5] = ($this->h - $h - $y) * $this->k; 26813 $this->Transform($tm); 26814 // set object 26815 $this->_out('/'.$id.' Do'); 26816 $this->StopTransform(); 26817 // add annotations 26818 if (!empty($this->xobjects[$id]['annotations'])) { 26819 foreach ($this->xobjects[$id]['annotations'] as $annot) { 26820 // transform original coordinates 26821 $coordlt = $this->getTransformationMatrixProduct($tm, array(1, 0, 0, 1, ($annot['x'] * $this->k), (-$annot['y'] * $this->k))); 26822 $ax = ($coordlt[4] / $this->k); 26823 $ay = ($this->h - $h - ($coordlt[5] / $this->k)); 26824 $coordrb = $this->getTransformationMatrixProduct($tm, array(1, 0, 0, 1, (($annot['x'] + $annot['w']) * $this->k), ((-$annot['y'] - $annot['h']) * $this->k))); 26825 $aw = ($coordrb[4] / $this->k) - $ax; 26826 $ah = ($this->h - $h - ($coordrb[5] / $this->k)) - $ay; 26827 $this->Annotation($ax, $ay, $aw, $ah, $annot['text'], $annot['opt'], $annot['spaces']); 26828 } 26829 } 26830 // set pointer to align the next text/objects 26831 switch($align) { 26832 case 'T': { 26833 $this->y = $y; 26834 $this->x = $rb_x; 26835 break; 26836 } 26837 case 'M': { 26838 $this->y = $y + round($h/2); 26839 $this->x = $rb_x; 26840 break; 26841 } 26842 case 'B': { 26843 $this->y = $rb_y; 26844 $this->x = $rb_x; 26845 break; 26846 } 26847 case 'N': { 26848 $this->SetY($rb_y); 26849 break; 26850 } 26851 default:{ 26852 break; 26853 } 26854 } 26855 } 26856 26864 public function setFontStretching($perc=100) { 26865 $this->font_stretching = $perc; 26866 } 26867 26875 public function getFontStretching() { 26876 return $this->font_stretching; 26877 } 26878 26886 public function setFontSpacing($spacing=0) { 26887 $this->font_spacing = $spacing; 26888 } 26889 26897 public function getFontSpacing() { 26898 return $this->font_spacing; 26899 } 26900 26909 public function getPageRegions() { 26910 return $this->page_regions; 26911 } 26912 26924 public function setPageRegions($regions=array()) { 26925 // empty current regions array 26926 $this->page_regions = array(); 26927 // add regions 26928 foreach ($regions as $data) { 26929 $this->addPageRegion($data); 26930 } 26931 } 26932 26944 public function addPageRegion($region) { 26945 if (!isset($region['page']) OR empty($region['page'])) { 26946 $region['page'] = $this->page; 26947 } 26948 if (isset($region['xt']) AND isset($region['xb']) AND ($region['xt'] > 0) AND ($region['xb'] > 0) 26949 AND isset($region['yt']) AND isset($region['yb']) AND ($region['yt'] >= 0) AND ($region['yt'] < $region['yb']) 26950 AND isset($region['side']) AND (($region['side'] == 'L') OR ($region['side'] == 'R'))) { 26951 $this->page_regions[] = $region; 26952 } 26953 } 26954 26963 public function removePageRegion($key) { 26964 if (isset($this->page_regions[$key])) { 26965 unset($this->page_regions[$key]); 26966 } 26967 } 26968 26981 protected function checkPageRegions($h, $x, $y) { 26982 // set default values 26983 if ($x === '') { 26984 $x = $this->x; 26985 } 26986 if ($y === '') { 26987 $y = $this->y; 26988 } 26989 if (empty($this->page_regions)) { 26990 // no page regions defined 26991 return array($x, $y); 26992 } 26993 if (empty($h)) { 26994 $h = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B']; 26995 } 26996 // check for page break 26997 if ($this->checkPageBreak($h, $y)) { 26998 // the content will be printed on a new page 26999 $x = $this->x; 27000 $y = $this->y; 27001 } 27002 if ($this->num_columns > 1) { 27003 if ($this->rtl) { 27004 $this->lMargin = $this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w']; 27005 } else { 27006 $this->rMargin = $this->w - $this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w']; 27007 } 27008 } else { 27009 if ($this->rtl) { 27010 $this->lMargin = $this->original_lMargin; 27011 } else { 27012 $this->rMargin = $this->original_rMargin; 27013 } 27014 } 27015 // adjust coordinates and page margins 27016 foreach ($this->page_regions as $regid => $regdata) { 27017 if ($regdata['page'] == $this->page) { 27018 // check region boundaries 27019 if (($y > ($regdata['yt'] - $h)) AND ($y <= $regdata['yb'])) { 27020 // Y is inside the region 27021 $minv = ($regdata['xb'] - $regdata['xt']) / ($regdata['yb'] - $regdata['yt']); // inverse of angular coefficient 27022 $yt = max($y, $regdata['yt']); 27023 $yb = min(($yt + $h), $regdata['yb']); 27024 $xt = (($yt - $regdata['yt']) * $minv) + $regdata['xt']; 27025 $xb = (($yb - $regdata['yt']) * $minv) + $regdata['xt']; 27026 if ($regdata['side'] == 'L') { // left side 27027 $new_margin = max($xt, $xb); 27028 if ($this->lMargin < $new_margin) { 27029 if ($this->rtl) { 27030 // adjust left page margin 27031 $this->lMargin = $new_margin; 27032 } 27033 if ($x < $new_margin) { 27034 // adjust x position 27035 $x = $new_margin; 27036 if ($new_margin > ($this->w - $this->rMargin)) { 27037 // adjust y position 27038 $y = $regdata['yb'] - $h; 27039 } 27040 } 27041 } 27042 } elseif ($regdata['side'] == 'R') { // right side 27043 $new_margin = min($xt, $xb); 27044 if (($this->w - $this->rMargin) > $new_margin) { 27045 if (!$this->rtl) { 27046 // adjust right page margin 27047 $this->rMargin = ($this->w - $new_margin); 27048 } 27049 if ($x > $new_margin) { 27050 // adjust x position 27051 $x = $new_margin; 27052 if ($new_margin > $this->lMargin) { 27053 // adjust y position 27054 $y = $regdata['yb'] - $h; 27055 } 27056 } 27057 } 27058 } 27059 } 27060 } 27061 } 27062 return array($x, $y); 27063 } 27064 27065 // --- SVG METHODS --------------------------------------------------------- 27066 27084 public function ImageSVG($file, $x='', $y='', $w=0, $h=0, $link='', $align='', $palign='', $border=0, $fitonpage=false) { 27085 if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) { 27086 // convert SVG to raster image using GD or ImageMagick libraries 27087 return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false); 27088 } 27089 if ($file{0} === '@') { // image from string 27090 $this->svgdir = ''; 27091 $svgdata = substr($file, 1); 27092 } else { // SVG file 27093 $this->svgdir = dirname($file); 27094 $svgdata = file_get_contents($file); 27095 } 27096 if ($svgdata === false) { 27097 $this->Error('SVG file not found: '.$file); 27098 } 27099 if ($x === '') { 27100 $x = $this->x; 27101 } 27102 if ($y === '') { 27103 $y = $this->y; 27104 } 27105 // check page for no-write regions and adapt page margins if necessary 27106 list($x, $y) = $this->checkPageRegions($h, $x, $y); 27107 $k = $this->k; 27108 $ox = 0; 27109 $oy = 0; 27110 $ow = $w; 27111 $oh = $h; 27112 $aspect_ratio_align = 'xMidYMid'; 27113 $aspect_ratio_ms = 'meet'; 27114 $regs = array(); 27115 // get original image width and height 27116 preg_match('/<svg([^>]*)>/si', $svgdata, $regs); 27117 if (isset($regs[1]) AND !empty($regs[1])) { 27118 $tmp = array(); 27119 if (preg_match('/[\s]+x[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) { 27120 $ox = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false); 27121 } 27122 $tmp = array(); 27123 if (preg_match('/[\s]+y[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) { 27124 $oy = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false); 27125 } 27126 $tmp = array(); 27127 if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) { 27128 $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false); 27129 } 27130 $tmp = array(); 27131 if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) { 27132 $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false); 27133 } 27134 $tmp = array(); 27135 $view_box = array(); 27136 if (preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.\-]+)[\s]+([0-9\.\-]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $regs[1], $tmp)) { 27137 if (count($tmp) == 5) { 27138 array_shift($tmp); 27139 foreach ($tmp as $key => $val) { 27140 $view_box[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false); 27141 } 27142 $ox = $view_box[0]; 27143 $oy = $view_box[1]; 27144 } 27145 // get aspect ratio 27146 $tmp = array(); 27147 if (preg_match('/[\s]+preserveAspectRatio[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) { 27148 $aspect_ratio = preg_split('/[\s]+/si', $tmp[1]); 27149 switch (count($aspect_ratio)) { 27150 case 3: { 27151 $aspect_ratio_align = $aspect_ratio[1]; 27152 $aspect_ratio_ms = $aspect_ratio[2]; 27153 break; 27154 } 27155 case 2: { 27156 $aspect_ratio_align = $aspect_ratio[0]; 27157 $aspect_ratio_ms = $aspect_ratio[1]; 27158 break; 27159 } 27160 case 1: { 27161 $aspect_ratio_align = $aspect_ratio[0]; 27162 $aspect_ratio_ms = 'meet'; 27163 break; 27164 } 27165 } 27166 } 27167 } 27168 } 27169 // calculate image width and height on document 27170 if (($w <= 0) AND ($h <= 0)) { 27171 // convert image size to document unit 27172 $w = $ow; 27173 $h = $oh; 27174 } elseif ($w <= 0) { 27175 $w = $h * $ow / $oh; 27176 } elseif ($h <= 0) { 27177 $h = $w * $oh / $ow; 27178 } 27179 // fit the image on available space 27180 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage); 27181 if ($this->rasterize_vector_images) { 27182 // convert SVG to raster image using GD or ImageMagick libraries 27183 return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false); 27184 } 27185 // set alignment 27186 $this->img_rb_y = $y + $h; 27187 // set alignment 27188 if ($this->rtl) { 27189 if ($palign == 'L') { 27190 $ximg = $this->lMargin; 27191 } elseif ($palign == 'C') { 27192 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 27193 } elseif ($palign == 'R') { 27194 $ximg = $this->w - $this->rMargin - $w; 27195 } else { 27196 $ximg = $x - $w; 27197 } 27198 $this->img_rb_x = $ximg; 27199 } else { 27200 if ($palign == 'L') { 27201 $ximg = $this->lMargin; 27202 } elseif ($palign == 'C') { 27203 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; 27204 } elseif ($palign == 'R') { 27205 $ximg = $this->w - $this->rMargin - $w; 27206 } else { 27207 $ximg = $x; 27208 } 27209 $this->img_rb_x = $ximg + $w; 27210 } 27211 // store current graphic vars 27212 $gvars = $this->getGraphicVars(); 27213 // store SVG position and scale factors 27214 $svgoffset_x = ($ximg - $ox) * $this->k; 27215 $svgoffset_y = -($y - $oy) * $this->k; 27216 if (isset($view_box[2]) AND ($view_box[2] > 0) AND ($view_box[3] > 0)) { 27217 $ow = $view_box[2]; 27218 $oh = $view_box[3]; 27219 } else { 27220 if ($ow <= 0) { 27221 $ow = $w; 27222 } 27223 if ($oh <= 0) { 27224 $oh = $h; 27225 } 27226 } 27227 $svgscale_x = $w / $ow; 27228 $svgscale_y = $h / $oh; 27229 // scaling and alignment 27230 if ($aspect_ratio_align != 'none') { 27231 // store current scaling values 27232 $svgscale_old_x = $svgscale_x; 27233 $svgscale_old_y = $svgscale_y; 27234 // force uniform scaling 27235 if ($aspect_ratio_ms == 'slice') { 27236 // the entire viewport is covered by the viewBox 27237 if ($svgscale_x > $svgscale_y) { 27238 $svgscale_y = $svgscale_x; 27239 } elseif ($svgscale_x < $svgscale_y) { 27240 $svgscale_x = $svgscale_y; 27241 } 27242 } else { // meet 27243 // the entire viewBox is visible within the viewport 27244 if ($svgscale_x < $svgscale_y) { 27245 $svgscale_y = $svgscale_x; 27246 } elseif ($svgscale_x > $svgscale_y) { 27247 $svgscale_x = $svgscale_y; 27248 } 27249 } 27250 // correct X alignment 27251 switch (substr($aspect_ratio_align, 1, 3)) { 27252 case 'Min': { 27253 // do nothing 27254 break; 27255 } 27256 case 'Max': { 27257 $svgoffset_x += (($w * $this->k) - ($ow * $this->k * $svgscale_x)); 27258 break; 27259 } 27260 default: 27261 case 'Mid': { 27262 $svgoffset_x += ((($w * $this->k) - ($ow * $this->k * $svgscale_x)) / 2); 27263 break; 27264 } 27265 } 27266 // correct Y alignment 27267 switch (substr($aspect_ratio_align, 5)) { 27268 case 'Min': { 27269 // do nothing 27270 break; 27271 } 27272 case 'Max': { 27273 $svgoffset_y -= (($h * $this->k) - ($oh * $this->k * $svgscale_y)); 27274 break; 27275 } 27276 default: 27277 case 'Mid': { 27278 $svgoffset_y -= ((($h * $this->k) - ($oh * $this->k * $svgscale_y)) / 2); 27279 break; 27280 } 27281 } 27282 } 27283 // store current page break mode 27284 $page_break_mode = $this->AutoPageBreak; 27285 $page_break_margin = $this->getBreakMargin(); 27286 $cell_padding = $this->cell_padding; 27287 $this->SetCellPadding(0); 27288 $this->SetAutoPageBreak(false); 27289 // save the current graphic state 27290 $this->_out('q'.$this->epsmarker); 27291 // set initial clipping mask 27292 $this->Rect($x, $y, $w, $h, 'CNZ', array(), array()); 27293 // scale and translate 27294 $e = $ox * $this->k * (1 - $svgscale_x); 27295 $f = ($this->h - $oy) * $this->k * (1 - $svgscale_y); 27296 $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $svgscale_x, 0, 0, $svgscale_y, $e + $svgoffset_x, $f + $svgoffset_y)); 27297 // creates a new XML parser to be used by the other XML functions 27298 $this->parser = xml_parser_create('UTF-8'); 27299 // the following function allows to use parser inside object 27300 xml_set_object($this->parser, $this); 27301 // disable case-folding for this XML parser 27302 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0); 27303 // sets the element handler functions for the XML parser 27304 xml_set_element_handler($this->parser, 'startSVGElementHandler', 'endSVGElementHandler'); 27305 // sets the character data handler function for the XML parser 27306 xml_set_character_data_handler($this->parser, 'segSVGContentHandler'); 27307 // start parsing an XML document 27308 if (!xml_parse($this->parser, $svgdata)) { 27309 $error_message = sprintf('SVG Error: %s at line %d', xml_error_string(xml_get_error_code($this->parser)), xml_get_current_line_number($this->parser)); 27310 $this->Error($error_message); 27311 } 27312 // free this XML parser 27313 xml_parser_free($this->parser); 27314 // restore previous graphic state 27315 $this->_out($this->epsmarker.'Q'); 27316 // restore graphic vars 27317 $this->setGraphicVars($gvars); 27318 $this->lasth = $gvars['lasth']; 27319 if (!empty($border)) { 27320 $bx = $this->x; 27321 $by = $this->y; 27322 $this->x = $ximg; 27323 if ($this->rtl) { 27324 $this->x += $w; 27325 } 27326 $this->y = $y; 27327 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true); 27328 $this->x = $bx; 27329 $this->y = $by; 27330 } 27331 if ($link) { 27332 $this->Link($ximg, $y, $w, $h, $link, 0); 27333 } 27334 // set pointer to align the next text/objects 27335 switch($align) { 27336 case 'T':{ 27337 $this->y = $y; 27338 $this->x = $this->img_rb_x; 27339 break; 27340 } 27341 case 'M':{ 27342 $this->y = $y + round($h/2); 27343 $this->x = $this->img_rb_x; 27344 break; 27345 } 27346 case 'B':{ 27347 $this->y = $this->img_rb_y; 27348 $this->x = $this->img_rb_x; 27349 break; 27350 } 27351 case 'N':{ 27352 $this->SetY($this->img_rb_y); 27353 break; 27354 } 27355 default:{ 27356 // restore pointer to starting position 27357 $this->x = $gvars['x']; 27358 $this->y = $gvars['y']; 27359 $this->page = $gvars['page']; 27360 $this->current_column = $gvars['current_column']; 27361 $this->tMargin = $gvars['tMargin']; 27362 $this->bMargin = $gvars['bMargin']; 27363 $this->w = $gvars['w']; 27364 $this->h = $gvars['h']; 27365 $this->wPt = $gvars['wPt']; 27366 $this->hPt = $gvars['hPt']; 27367 $this->fwPt = $gvars['fwPt']; 27368 $this->fhPt = $gvars['fhPt']; 27369 break; 27370 } 27371 } 27372 $this->endlinex = $this->img_rb_x; 27373 // restore page break 27374 $this->SetAutoPageBreak($page_break_mode, $page_break_margin); 27375 $this->cell_padding = $cell_padding; 27376 } 27377 27386 protected function getSVGTransformMatrix($attribute) { 27387 // identity matrix 27388 $tm = array(1, 0, 0, 1, 0, 0); 27389 $transform = array(); 27390 if (preg_match_all('/(matrix|translate|scale|rotate|skewX|skewY)[\s]*\(([^\)]+)\)/si', $attribute, $transform, PREG_SET_ORDER) > 0) { 27391 foreach ($transform as $key => $data) { 27392 if (!empty($data[2])) { 27393 $a = 1; 27394 $b = 0; 27395 $c = 0; 27396 $d = 1; 27397 $e = 0; 27398 $f = 0; 27399 $regs = array(); 27400 switch ($data[1]) { 27401 case 'matrix': { 27402 if (preg_match('/([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)/si', $data[2], $regs)) { 27403 $a = $regs[1]; 27404 $b = $regs[2]; 27405 $c = $regs[3]; 27406 $d = $regs[4]; 27407 $e = $regs[5]; 27408 $f = $regs[6]; 27409 } 27410 break; 27411 } 27412 case 'translate': { 27413 if (preg_match('/([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)/si', $data[2], $regs)) { 27414 $e = $regs[1]; 27415 $f = $regs[2]; 27416 } elseif (preg_match('/([a-z0-9\-\.]+)/si', $data[2], $regs)) { 27417 $e = $regs[1]; 27418 } 27419 break; 27420 } 27421 case 'scale': { 27422 if (preg_match('/([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)/si', $data[2], $regs)) { 27423 $a = $regs[1]; 27424 $d = $regs[2]; 27425 } elseif (preg_match('/([a-z0-9\-\.]+)/si', $data[2], $regs)) { 27426 $a = $regs[1]; 27427 $d = $a; 27428 } 27429 break; 27430 } 27431 case 'rotate': { 27432 if (preg_match('/([0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)/si', $data[2], $regs)) { 27433 $ang = deg2rad($regs[1]); 27434 $x = $regs[2]; 27435 $y = $regs[3]; 27436 $a = cos($ang); 27437 $b = sin($ang); 27438 $c = -$b; 27439 $d = $a; 27440 $e = ($x * (1 - $a)) - ($y * $c); 27441 $f = ($y * (1 - $d)) - ($x * $b); 27442 } elseif (preg_match('/([0-9\-\.]+)/si', $data[2], $regs)) { 27443 $ang = deg2rad($regs[1]); 27444 $a = cos($ang); 27445 $b = sin($ang); 27446 $c = -$b; 27447 $d = $a; 27448 $e = 0; 27449 $f = 0; 27450 } 27451 break; 27452 } 27453 case 'skewX': { 27454 if (preg_match('/([0-9\-\.]+)/si', $data[2], $regs)) { 27455 $c = tan(deg2rad($regs[1])); 27456 } 27457 break; 27458 } 27459 case 'skewY': { 27460 if (preg_match('/([0-9\-\.]+)/si', $data[2], $regs)) { 27461 $b = tan(deg2rad($regs[1])); 27462 } 27463 break; 27464 } 27465 } 27466 $tm = $this->getTransformationMatrixProduct($tm, array($a, $b, $c, $d, $e, $f)); 27467 } 27468 } 27469 } 27470 return $tm; 27471 } 27472 27482 protected function getTransformationMatrixProduct($ta, $tb) { 27483 $tm = array(); 27484 $tm[0] = ($ta[0] * $tb[0]) + ($ta[2] * $tb[1]); 27485 $tm[1] = ($ta[1] * $tb[0]) + ($ta[3] * $tb[1]); 27486 $tm[2] = ($ta[0] * $tb[2]) + ($ta[2] * $tb[3]); 27487 $tm[3] = ($ta[1] * $tb[2]) + ($ta[3] * $tb[3]); 27488 $tm[4] = ($ta[0] * $tb[4]) + ($ta[2] * $tb[5]) + $ta[4]; 27489 $tm[5] = ($ta[1] * $tb[4]) + ($ta[3] * $tb[5]) + $ta[5]; 27490 return $tm; 27491 } 27492 27500 protected function convertSVGtMatrix($tm) { 27501 $a = $tm[0]; 27502 $b = -$tm[1]; 27503 $c = -$tm[2]; 27504 $d = $tm[3]; 27505 $e = $this->getHTMLUnitToUnits($tm[4], 1, $this->svgunit, false) * $this->k; 27506 $f = -$this->getHTMLUnitToUnits($tm[5], 1, $this->svgunit, false) * $this->k; 27507 $x = 0; 27508 $y = $this->h * $this->k; 27509 $e = ($x * (1 - $a)) - ($y * $c) + $e; 27510 $f = ($y * (1 - $d)) - ($x * $b) + $f; 27511 return array($a, $b, $c, $d, $e, $f); 27512 } 27513 27520 protected function SVGTransform($tm) { 27521 $this->Transform($this->convertSVGtMatrix($tm)); 27522 } 27523 27539 protected function setSVGStyles($svgstyle, $prevsvgstyle, $x=0, $y=0, $w=1, $h=1, $clip_function='', $clip_params=array()) { 27540 $objstyle = ''; 27541 $minlen = (0.01 / $this->k); // minimum acceptable length (3 point) 27542 if (!isset($svgstyle['opacity'])) { 27543 return $objstyle; 27544 } 27545 // clip-path 27546 $regs = array(); 27547 if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['clip-path'], $regs)) { 27548 $clip_path = $this->svgclippaths[$regs[1]]; 27549 foreach ($clip_path as $cp) { 27550 $this->startSVGElementHandler('clip-path', $cp['name'], $cp['attribs'], $cp['tm']); 27551 } 27552 } 27553 // opacity 27554 if ($svgstyle['opacity'] != 1) { 27555 $this->SetAlpha($svgstyle['opacity']); 27556 } 27557 // color 27558 $fill_color = $this->convertHTMLColorToDec($svgstyle['color']); 27559 $this->SetFillColorArray($fill_color); 27560 // text color 27561 $text_color = $this->convertHTMLColorToDec($svgstyle['text-color']); 27562 $this->SetTextColorArray($text_color); 27563 // clip 27564 if (preg_match('/rect\(([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)\)/si', $svgstyle['clip'], $regs)) { 27565 $top = (isset($regs[1])?$this->getHTMLUnitToUnits($regs[1], 0, $this->svgunit, false):0); 27566 $right = (isset($regs[2])?$this->getHTMLUnitToUnits($regs[2], 0, $this->svgunit, false):0); 27567 $bottom = (isset($regs[3])?$this->getHTMLUnitToUnits($regs[3], 0, $this->svgunit, false):0); 27568 $left = (isset($regs[4])?$this->getHTMLUnitToUnits($regs[4], 0, $this->svgunit, false):0); 27569 $cx = $x + $left; 27570 $cy = $y + $top; 27571 $cw = $w - $left - $right; 27572 $ch = $h - $top - $bottom; 27573 if ($svgstyle['clip-rule'] == 'evenodd') { 27574 $clip_rule = 'CNZ'; 27575 } else { 27576 $clip_rule = 'CEO'; 27577 } 27578 $this->Rect($cx, $cy, $cw, $ch, $clip_rule, array(), array()); 27579 } 27580 // fill 27581 $regs = array(); 27582 if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['fill'], $regs)) { 27583 // gradient 27584 $gradient = $this->svggradients[$regs[1]]; 27585 if (isset($gradient['xref'])) { 27586 // reference to another gradient definition 27587 $newgradient = $this->svggradients[$gradient['xref']]; 27588 $newgradient['coords'] = $gradient['coords']; 27589 $newgradient['mode'] = $gradient['mode']; 27590 $newgradient['gradientUnits'] = $gradient['gradientUnits']; 27591 if (isset($gradient['gradientTransform'])) { 27592 $newgradient['gradientTransform'] = $gradient['gradientTransform']; 27593 } 27594 $gradient = $newgradient; 27595 } 27596 //save current Graphic State 27597 $this->_out('q'); 27598 //set clipping area 27599 if (!empty($clip_function) AND method_exists($this, $clip_function)) { 27600 $bbox = call_user_func_array(array($this, $clip_function), $clip_params); 27601 if (is_array($bbox) AND (count($bbox) == 4)) { 27602 list($x, $y, $w, $h) = $bbox; 27603 } 27604 } 27605 if ($gradient['mode'] == 'measure') { 27606 if (isset($gradient['gradientTransform']) AND !empty($gradient['gradientTransform'])) { 27607 $gtm = $gradient['gradientTransform']; 27608 // apply transformation matrix 27609 $xa = ($gtm[0] * $gradient['coords'][0]) + ($gtm[2] * $gradient['coords'][1]) + $gtm[4]; 27610 $ya = ($gtm[1] * $gradient['coords'][0]) + ($gtm[3] * $gradient['coords'][1]) + $gtm[5]; 27611 $xb = ($gtm[0] * $gradient['coords'][2]) + ($gtm[2] * $gradient['coords'][3]) + $gtm[4]; 27612 $yb = ($gtm[1] * $gradient['coords'][2]) + ($gtm[3] * $gradient['coords'][3]) + $gtm[5]; 27613 if (isset($gradient['coords'][4])) { 27614 $gradient['coords'][4] = sqrt(pow(($gtm[0] * $gradient['coords'][4]), 2) + pow(($gtm[1] * $gradient['coords'][4]), 2)); 27615 } 27616 $gradient['coords'][0] = $xa; 27617 $gradient['coords'][1] = $ya; 27618 $gradient['coords'][2] = $xb; 27619 $gradient['coords'][3] = $yb; 27620 } 27621 // convert SVG coordinates to user units 27622 $gradient['coords'][0] = $this->getHTMLUnitToUnits($gradient['coords'][0], 0, $this->svgunit, false); 27623 $gradient['coords'][1] = $this->getHTMLUnitToUnits($gradient['coords'][1], 0, $this->svgunit, false); 27624 $gradient['coords'][2] = $this->getHTMLUnitToUnits($gradient['coords'][2], 0, $this->svgunit, false); 27625 $gradient['coords'][3] = $this->getHTMLUnitToUnits($gradient['coords'][3], 0, $this->svgunit, false); 27626 if (isset($gradient['coords'][4])) { 27627 $gradient['coords'][4] = $this->getHTMLUnitToUnits($gradient['coords'][4], 0, $this->svgunit, false); 27628 } 27629 // shift units 27630 if ($gradient['gradientUnits'] == 'objectBoundingBox') { 27631 // convert to SVG coordinate system 27632 $gradient['coords'][0] += $x; 27633 $gradient['coords'][1] += $y; 27634 $gradient['coords'][2] += $x; 27635 $gradient['coords'][3] += $y; 27636 } 27637 if ($w <= $minlen) { 27638 $w = $minlen; 27639 } 27640 if ($h <= $minlen) { 27641 $h = $minlen; 27642 } 27643 // calculate percentages 27644 $gradient['coords'][0] = ($gradient['coords'][0] - $x) / $w; 27645 $gradient['coords'][1] = ($gradient['coords'][1] - $y) / $h; 27646 $gradient['coords'][2] = ($gradient['coords'][2] - $x) / $w; 27647 $gradient['coords'][3] = ($gradient['coords'][3] - $y) / $h; 27648 if (isset($gradient['coords'][4])) { 27649 $gradient['coords'][4] /= $w; 27650 } 27651 } elseif ($gradient['mode'] == 'percentage') { 27652 foreach($gradient['coords'] as $key => $val) { 27653 $gradient['coords'][$key] = (intval($val) / 100); 27654 } 27655 } 27656 // fix values 27657 foreach($gradient['coords'] as $key => $val) { 27658 if ($val < 0) { 27659 $gradient['coords'][$key] = 0; 27660 } elseif ($val > 1) { 27661 $gradient['coords'][$key] = 1; 27662 } 27663 } 27664 if (($gradient['type'] == 2) AND ($gradient['coords'][0] == $gradient['coords'][2]) AND ($gradient['coords'][1] == $gradient['coords'][3])) { 27665 // single color (no shading) 27666 $gradient['coords'][0] = 1; 27667 $gradient['coords'][1] = 0; 27668 $gradient['coords'][2] = 0.999; 27669 $gradient['coords'][3] = 0; 27670 } 27671 // swap Y coordinates 27672 $tmp = $gradient['coords'][1]; 27673 $gradient['coords'][1] = $gradient['coords'][3]; 27674 $gradient['coords'][3] = $tmp; 27675 // set transformation map for gradient 27676 if ($gradient['type'] == 3) { 27677 // gradient is always circular 27678 $cy = $this->h - $y - ($gradient['coords'][1] * ($w + $h)); 27679 $this->_out(sprintf('%.3F 0 0 %.3F %.3F %.3F cm', $w*$this->k, $w*$this->k, $x*$this->k, $cy*$this->k)); 27680 } else { 27681 $this->_out(sprintf('%.3F 0 0 %.3F %.3F %.3F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k)); 27682 } 27683 if (count($gradient['stops']) > 1) { 27684 $this->Gradient($gradient['type'], $gradient['coords'], $gradient['stops'], array(), false); 27685 } 27686 } elseif ($svgstyle['fill'] != 'none') { 27687 $fill_color = $this->convertHTMLColorToDec($svgstyle['fill']); 27688 if ($svgstyle['fill-opacity'] != 1) { 27689 $this->SetAlpha($svgstyle['fill-opacity']); 27690 } 27691 $this->SetFillColorArray($fill_color); 27692 if ($svgstyle['fill-rule'] == 'evenodd') { 27693 $objstyle .= 'F*'; 27694 } else { 27695 $objstyle .= 'F'; 27696 } 27697 } 27698 // stroke 27699 if ($svgstyle['stroke'] != 'none') { 27700 $stroke_style = array( 27701 'color' => $this->convertHTMLColorToDec($svgstyle['stroke']), 27702 'width' => $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false), 27703 'cap' => $svgstyle['stroke-linecap'], 27704 'join' => $svgstyle['stroke-linejoin'] 27705 ); 27706 if (isset($svgstyle['stroke-dasharray']) AND !empty($svgstyle['stroke-dasharray']) AND ($svgstyle['stroke-dasharray'] != 'none')) { 27707 $stroke_style['dash'] = $svgstyle['stroke-dasharray']; 27708 } 27709 $this->SetLineStyle($stroke_style); 27710 $objstyle .= 'D'; 27711 } 27712 // font 27713 $regs = array(); 27714 if (!empty($svgstyle['font'])) { 27715 if (preg_match('/font-family[\s]*:[\s]*([^\;\"]*)/si', $svgstyle['font'], $regs)) { 27716 $font_family = $this->getFontFamilyName($regs[1]); 27717 } else { 27718 $font_family = $svgstyle['font-family']; 27719 } 27720 if (preg_match('/font-size[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) { 27721 $font_size = trim($regs[1]); 27722 } else { 27723 $font_size = $svgstyle['font-size']; 27724 } 27725 if (preg_match('/font-style[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) { 27726 $font_style = trim($regs[1]); 27727 } else { 27728 $font_style = $svgstyle['font-style']; 27729 } 27730 if (preg_match('/font-weight[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) { 27731 $font_weight = trim($regs[1]); 27732 } else { 27733 $font_weight = $svgstyle['font-weight']; 27734 } 27735 if (preg_match('/font-stretch[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) { 27736 $font_stretch = trim($regs[1]); 27737 } else { 27738 $font_stretch = $svgstyle['font-stretch']; 27739 } 27740 if (preg_match('/letter-spacing[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) { 27741 $font_spacing = trim($regs[1]); 27742 } else { 27743 $font_spacing = $svgstyle['letter-spacing']; 27744 } 27745 } else { 27746 $font_family = $this->getFontFamilyName($svgstyle['font-family']); 27747 $font_size = $svgstyle['font-size']; 27748 $font_style = $svgstyle['font-style']; 27749 $font_weight = $svgstyle['font-weight']; 27750 $font_stretch = $svgstyle['font-stretch']; 27751 $font_spacing = $svgstyle['letter-spacing']; 27752 } 27753 $font_size = $this->getHTMLUnitToUnits($font_size, $prevsvgstyle['font-size'], $this->svgunit, false) * $this->k; 27754 $font_stretch = $this->getCSSFontStretching($font_stretch, $svgstyle['font-stretch']); 27755 $font_spacing = $this->getCSSFontSpacing($font_spacing, $svgstyle['letter-spacing']); 27756 switch ($font_style) { 27757 case 'italic': { 27758 $font_style = 'I'; 27759 break; 27760 } 27761 case 'oblique': { 27762 $font_style = 'I'; 27763 break; 27764 } 27765 default: 27766 case 'normal': { 27767 $font_style = ''; 27768 break; 27769 } 27770 } 27771 switch ($font_weight) { 27772 case 'bold': 27773 case 'bolder': { 27774 $font_style .= 'B'; 27775 break; 27776 } 27777 } 27778 switch ($svgstyle['text-decoration']) { 27779 case 'underline': { 27780 $font_style .= 'U'; 27781 break; 27782 } 27783 case 'overline': { 27784 $font_style .= 'O'; 27785 break; 27786 } 27787 case 'line-through': { 27788 $font_style .= 'D'; 27789 break; 27790 } 27791 default: 27792 case 'none': { 27793 break; 27794 } 27795 } 27796 $this->SetFont($font_family, $font_style, $font_size); 27797 $this->setFontStretching($font_stretch); 27798 $this->setFontSpacing($font_spacing); 27799 return $objstyle; 27800 } 27801 27820 protected function SVGPath($d, $style='') { 27821 // set fill/stroke style 27822 $op = $this->getPathPaintOperator($style, ''); 27823 if (empty($op)) { 27824 return; 27825 } 27826 $paths = array(); 27827 $d = preg_replace('/([0-9ACHLMQSTVZ])([\-\+])/si', '\\1 \\2', $d); 27828 preg_match_all('/([ACHLMQSTVZ])[\s]*([^ACHLMQSTVZ\"]*)/si', $d, $paths, PREG_SET_ORDER); 27829 $x = 0; 27830 $y = 0; 27831 $x1 = 0; 27832 $y1 = 0; 27833 $x2 = 0; 27834 $y2 = 0; 27835 $xmin = 2147483647; 27836 $xmax = 0; 27837 $ymin = 2147483647; 27838 $ymax = 0; 27839 $relcoord = false; 27840 $minlen = (0.01 / $this->k); // minimum acceptable length (3 point) 27841 $firstcmd = true; // used to print first point 27842 // draw curve pieces 27843 foreach ($paths as $key => $val) { 27844 // get curve type 27845 $cmd = trim($val[1]); 27846 if (strtolower($cmd) == $cmd) { 27847 // use relative coordinated instead of absolute 27848 $relcoord = true; 27849 $xoffset = $x; 27850 $yoffset = $y; 27851 } else { 27852 $relcoord = false; 27853 $xoffset = 0; 27854 $yoffset = 0; 27855 } 27856 $params = array(); 27857 if (isset($val[2])) { 27858 // get curve parameters 27859 $rawparams = preg_split('/([\,\s]+)/si', trim($val[2])); 27860 $params = array(); 27861 foreach ($rawparams as $ck => $cp) { 27862 $params[$ck] = $this->getHTMLUnitToUnits($cp, 0, $this->svgunit, false); 27863 if (abs($params[$ck]) < $minlen) { 27864 // aproximate little values to zero 27865 $params[$ck] = 0; 27866 } 27867 } 27868 } 27869 // store current origin point 27870 $x0 = $x; 27871 $y0 = $y; 27872 switch (strtoupper($cmd)) { 27873 case 'M': { // moveto 27874 foreach ($params as $ck => $cp) { 27875 if (($ck % 2) == 0) { 27876 $x = $cp + $xoffset; 27877 } else { 27878 $y = $cp + $yoffset; 27879 if ($firstcmd OR (abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) { 27880 if ($ck == 1) { 27881 $this->_outPoint($x, $y); 27882 $firstcmd = false; 27883 } else { 27884 $this->_outLine($x, $y); 27885 } 27886 } 27887 $xmin = min($xmin, $x); 27888 $ymin = min($ymin, $y); 27889 $xmax = max($xmax, $x); 27890 $ymax = max($ymax, $y); 27891 if ($relcoord) { 27892 $xoffset = $x; 27893 $yoffset = $y; 27894 } 27895 } 27896 } 27897 break; 27898 } 27899 case 'L': { // lineto 27900 foreach ($params as $ck => $cp) { 27901 if (($ck % 2) == 0) { 27902 $x = $cp + $xoffset; 27903 } else { 27904 $y = $cp + $yoffset; 27905 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) { 27906 $this->_outLine($x, $y); 27907 } 27908 $xmin = min($xmin, $x); 27909 $ymin = min($ymin, $y); 27910 $xmax = max($xmax, $x); 27911 $ymax = max($ymax, $y); 27912 if ($relcoord) { 27913 $xoffset = $x; 27914 $yoffset = $y; 27915 } 27916 } 27917 } 27918 break; 27919 } 27920 case 'H': { // horizontal lineto 27921 foreach ($params as $ck => $cp) { 27922 $x = $cp + $xoffset; 27923 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) { 27924 $this->_outLine($x, $y); 27925 } 27926 $xmin = min($xmin, $x); 27927 $xmax = max($xmax, $x); 27928 if ($relcoord) { 27929 $xoffset = $x; 27930 } 27931 } 27932 break; 27933 } 27934 case 'V': { // vertical lineto 27935 foreach ($params as $ck => $cp) { 27936 $y = $cp + $yoffset; 27937 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) { 27938 $this->_outLine($x, $y); 27939 } 27940 $ymin = min($ymin, $y); 27941 $ymax = max($ymax, $y); 27942 if ($relcoord) { 27943 $yoffset = $y; 27944 } 27945 } 27946 break; 27947 } 27948 case 'C': { // curveto 27949 foreach ($params as $ck => $cp) { 27950 $params[$ck] = $cp; 27951 if ((($ck + 1) % 6) == 0) { 27952 $x1 = $params[($ck - 5)] + $xoffset; 27953 $y1 = $params[($ck - 4)] + $yoffset; 27954 $x2 = $params[($ck - 3)] + $xoffset; 27955 $y2 = $params[($ck - 2)] + $yoffset; 27956 $x = $params[($ck - 1)] + $xoffset; 27957 $y = $params[($ck)] + $yoffset; 27958 $this->_outCurve($x1, $y1, $x2, $y2, $x, $y); 27959 $xmin = min($xmin, $x, $x1, $x2); 27960 $ymin = min($ymin, $y, $y1, $y2); 27961 $xmax = max($xmax, $x, $x1, $x2); 27962 $ymax = max($ymax, $y, $y1, $y2); 27963 if ($relcoord) { 27964 $xoffset = $x; 27965 $yoffset = $y; 27966 } 27967 } 27968 } 27969 break; 27970 } 27971 case 'S': { // shorthand/smooth curveto 27972 foreach ($params as $ck => $cp) { 27973 $params[$ck] = $cp; 27974 if ((($ck + 1) % 4) == 0) { 27975 if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'C') OR (strtoupper($paths[($key - 1)][1]) == 'S'))) { 27976 $x1 = (2 * $x) - $x2; 27977 $y1 = (2 * $y) - $y2; 27978 } else { 27979 $x1 = $x; 27980 $y1 = $y; 27981 } 27982 $x2 = $params[($ck - 3)] + $xoffset; 27983 $y2 = $params[($ck - 2)] + $yoffset; 27984 $x = $params[($ck - 1)] + $xoffset; 27985 $y = $params[($ck)] + $yoffset; 27986 $this->_outCurve($x1, $y1, $x2, $y2, $x, $y); 27987 $xmin = min($xmin, $x, $x1, $x2); 27988 $ymin = min($ymin, $y, $y1, $y2); 27989 $xmax = max($xmax, $x, $x1, $x2); 27990 $ymax = max($ymax, $y, $y1, $y2); 27991 if ($relcoord) { 27992 $xoffset = $x; 27993 $yoffset = $y; 27994 } 27995 } 27996 } 27997 break; 27998 } 27999 case 'Q': { // quadratic Bézier curveto 28000 foreach ($params as $ck => $cp) { 28001 $params[$ck] = $cp; 28002 if ((($ck + 1) % 4) == 0) { 28003 // convert quadratic points to cubic points 28004 $x1 = $params[($ck - 3)] + $xoffset; 28005 $y1 = $params[($ck - 2)] + $yoffset; 28006 $xa = ($x + (2 * $x1)) / 3; 28007 $ya = ($y + (2 * $y1)) / 3; 28008 $x = $params[($ck - 1)] + $xoffset; 28009 $y = $params[($ck)] + $yoffset; 28010 $xb = ($x + (2 * $x1)) / 3; 28011 $yb = ($y + (2 * $y1)) / 3; 28012 $this->_outCurve($xa, $ya, $xb, $yb, $x, $y); 28013 $xmin = min($xmin, $x, $xa, $xb); 28014 $ymin = min($ymin, $y, $ya, $yb); 28015 $xmax = max($xmax, $x, $xa, $xb); 28016 $ymax = max($ymax, $y, $ya, $yb); 28017 if ($relcoord) { 28018 $xoffset = $x; 28019 $yoffset = $y; 28020 } 28021 } 28022 } 28023 break; 28024 } 28025 case 'T': { // shorthand/smooth quadratic Bézier curveto 28026 foreach ($params as $ck => $cp) { 28027 $params[$ck] = $cp; 28028 if (($ck % 2) != 0) { 28029 if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'Q') OR (strtoupper($paths[($key - 1)][1]) == 'T'))) { 28030 $x1 = (2 * $x) - $x1; 28031 $y1 = (2 * $y) - $y1; 28032 } else { 28033 $x1 = $x; 28034 $y1 = $y; 28035 } 28036 // convert quadratic points to cubic points 28037 $xa = ($x + (2 * $x1)) / 3; 28038 $ya = ($y + (2 * $y1)) / 3; 28039 $x = $params[($ck - 1)] + $xoffset; 28040 $y = $params[($ck)] + $yoffset; 28041 $xb = ($x + (2 * $x1)) / 3; 28042 $yb = ($y + (2 * $y1)) / 3; 28043 $this->_outCurve($xa, $ya, $xb, $yb, $x, $y); 28044 $xmin = min($xmin, $x, $xa, $xb); 28045 $ymin = min($ymin, $y, $ya, $yb); 28046 $xmax = max($xmax, $x, $xa, $xb); 28047 $ymax = max($ymax, $y, $ya, $yb); 28048 if ($relcoord) { 28049 $xoffset = $x; 28050 $yoffset = $y; 28051 } 28052 } 28053 } 28054 break; 28055 } 28056 case 'A': { // elliptical arc 28057 foreach ($params as $ck => $cp) { 28058 $params[$ck] = $cp; 28059 if ((($ck + 1) % 7) == 0) { 28060 $x0 = $x; 28061 $y0 = $y; 28062 $rx = abs($params[($ck - 6)]); 28063 $ry = abs($params[($ck - 5)]); 28064 $ang = -$rawparams[($ck - 4)]; 28065 $angle = deg2rad($ang); 28066 $fa = $rawparams[($ck - 3)]; // large-arc-flag 28067 $fs = $rawparams[($ck - 2)]; // sweep-flag 28068 $x = $params[($ck - 1)] + $xoffset; 28069 $y = $params[$ck] + $yoffset; 28070 if ((abs($x0 - $x) < $minlen) AND (abs($y0 - $y) < $minlen)) { 28071 // endpoints are almost identical 28072 $xmin = min($xmin, $x); 28073 $ymin = min($ymin, $y); 28074 $xmax = max($xmax, $x); 28075 $ymax = max($ymax, $y); 28076 } else { 28077 $cos_ang = cos($angle); 28078 $sin_ang = sin($angle); 28079 $a = (($x0 - $x) / 2); 28080 $b = (($y0 - $y) / 2); 28081 $xa = ($a * $cos_ang) - ($b * $sin_ang); 28082 $ya = ($a * $sin_ang) + ($b * $cos_ang); 28083 $rx2 = $rx * $rx; 28084 $ry2 = $ry * $ry; 28085 $xa2 = $xa * $xa; 28086 $ya2 = $ya * $ya; 28087 $delta = ($xa2 / $rx2) + ($ya2 / $ry2); 28088 if ($delta > 1) { 28089 $rx *= sqrt($delta); 28090 $ry *= sqrt($delta); 28091 $rx2 = $rx * $rx; 28092 $ry2 = $ry * $ry; 28093 } 28094 $numerator = (($rx2 * $ry2) - ($rx2 * $ya2) - ($ry2 * $xa2)); 28095 if ($numerator < 0) { 28096 $root = 0; 28097 } else { 28098 $root = sqrt($numerator / (($rx2 * $ya2) + ($ry2 * $xa2))); 28099 } 28100 if ($fa == $fs){ 28101 $root *= -1; 28102 } 28103 $cax = $root * (($rx * $ya) / $ry); 28104 $cay = -$root * (($ry * $xa) / $rx); 28105 // coordinates of ellipse center 28106 $cx = ($cax * $cos_ang) - ($cay * $sin_ang) + (($x0 + $x) / 2); 28107 $cy = ($cax * $sin_ang) + ($cay * $cos_ang) + (($y0 + $y) / 2); 28108 // get angles 28109 $angs = $this->getVectorsAngle(1, 0, (($xa - $cax) / $rx), (($cay - $ya) / $ry)); 28110 $dang = $this->getVectorsAngle((($xa - $cax) / $rx), (($ya - $cay) / $ry), ((-$xa - $cax) / $rx), ((-$ya - $cay) / $ry)); 28111 if (($fs == 0) AND ($dang > 0)) { 28112 $dang -= (2 * M_PI); 28113 } elseif (($fs == 1) AND ($dang < 0)) { 28114 $dang += (2 * M_PI); 28115 } 28116 $angf = $angs - $dang; 28117 if ((($fs == 0) AND ($angs > $angf)) OR (($fs == 1) AND ($angs < $angf))) { 28118 // reverse angles 28119 $tmp = $angs; 28120 $angs = $angf; 28121 $angf = $tmp; 28122 } 28123 $angs = round(rad2deg($angs), 6); 28124 $angf = round(rad2deg($angf), 6); 28125 // covent angles to positive values 28126 if (($angs < 0) AND ($angf < 0)) { 28127 $angs += 360; 28128 $angf += 360; 28129 } 28130 $pie = false; 28131 if (($key == 0) AND (isset($paths[($key + 1)][1])) AND (trim($paths[($key + 1)][1]) == 'z')) { 28132 $pie = true; 28133 } 28134 list($axmin, $aymin, $axmax, $aymax) = $this->_outellipticalarc($cx, $cy, $rx, $ry, $ang, $angs, $angf, $pie, 2, false, ($fs == 0), true); 28135 $xmin = min($xmin, $x, $axmin); 28136 $ymin = min($ymin, $y, $aymin); 28137 $xmax = max($xmax, $x, $axmax); 28138 $ymax = max($ymax, $y, $aymax); 28139 } 28140 if ($relcoord) { 28141 $xoffset = $x; 28142 $yoffset = $y; 28143 } 28144 } 28145 } 28146 break; 28147 } 28148 case 'Z': { 28149 $this->_out('h'); 28150 break; 28151 } 28152 } 28153 $firstcmd = false; 28154 } // end foreach 28155 if (!empty($op)) { 28156 $this->_out($op); 28157 } 28158 return array($xmin, $ymin, ($xmax - $xmin), ($ymax - $ymin)); 28159 } 28160 28171 protected function getVectorsAngle($x1, $y1, $x2, $y2) { 28172 $dprod = ($x1 * $x2) + ($y1 * $y2); 28173 $dist1 = sqrt(($x1 * $x1) + ($y1 * $y1)); 28174 $dist2 = sqrt(($x2 * $x2) + ($y2 * $y2)); 28175 $angle = acos($dprod / ($dist1 * $dist2)); 28176 if (is_nan($angle)) { 28177 $angle = M_PI; 28178 } 28179 if ((($x1 * $y2) - ($x2 * $y1)) < 0) { 28180 $angle *= -1; 28181 } 28182 return $angle; 28183 } 28184 28195 protected function startSVGElementHandler($parser, $name, $attribs, $ctm=array()) { 28196 // check if we are in clip mode 28197 if ($this->svgclipmode) { 28198 $this->svgclippaths[$this->svgclipid][] = array('name' => $name, 'attribs' => $attribs, 'tm' => $this->svgcliptm[$this->svgclipid]); 28199 return; 28200 } 28201 if ($this->svgdefsmode AND !in_array($name, array('clipPath', 'linearGradient', 'radialGradient', 'stop'))) { 28202 if (!isset($attribs['id'])) { 28203 $attribs['id'] = 'DF_'.(count($this->svgdefs) + 1); 28204 } 28205 $this->svgdefs[$attribs['id']] = array('name' => $name, 'attribs' => $attribs); 28206 return; 28207 } 28208 $clipping = false; 28209 if ($parser == 'clip-path') { 28210 // set clipping mode 28211 $clipping = true; 28212 } 28213 // get styling properties 28214 $prev_svgstyle = $this->svgstyles[(count($this->svgstyles) - 1)]; // previous style 28215 $svgstyle = $this->svgstyles[0]; // set default style 28216 if (isset($attribs['style']) AND !$this->empty_string($attribs['style'])) { 28217 // fix style for regular expression 28218 $attribs['style'] = ';'.$attribs['style']; 28219 } 28220 foreach ($prev_svgstyle as $key => $val) { 28221 if (in_array($key, $this->svginheritprop)) { 28222 // inherit previous value 28223 $svgstyle[$key] = $val; 28224 } 28225 if (isset($attribs[$key]) AND !$this->empty_string($attribs[$key])) { 28226 // specific attribute settings 28227 if ($attribs[$key] == 'inherit') { 28228 $svgstyle[$key] = $val; 28229 } else { 28230 $svgstyle[$key] = $attribs[$key]; 28231 } 28232 } elseif (isset($attribs['style']) AND !$this->empty_string($attribs['style'])) { 28233 // CSS style syntax 28234 $attrval = array(); 28235 if (preg_match('/[;\"\s]{1}'.$key.'[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval) AND isset($attrval[1])) { 28236 if ($attrval[1] == 'inherit') { 28237 $svgstyle[$key] = $val; 28238 } else { 28239 $svgstyle[$key] = $attrval[1]; 28240 } 28241 } 28242 } 28243 } 28244 // transformation matrix 28245 if (!empty($ctm)) { 28246 $tm = $ctm; 28247 } else { 28248 $tm = $this->svgstyles[(count($this->svgstyles) - 1)]['transfmatrix']; 28249 } 28250 if (isset($attribs['transform']) AND !empty($attribs['transform'])) { 28251 $tm = $this->getTransformationMatrixProduct($tm, $this->getSVGTransformMatrix($attribs['transform'])); 28252 } 28253 $svgstyle['transfmatrix'] = $tm; 28254 $invisible = false; 28255 if (($svgstyle['visibility'] == 'hidden') OR ($svgstyle['visibility'] == 'collapse') OR ($svgstyle['display'] == 'none')) { 28256 // the current graphics element is invisible (nothing is painted) 28257 $invisible = true; 28258 } 28259 // process tag 28260 switch($name) { 28261 case 'defs': { 28262 $this->svgdefsmode = true; 28263 break; 28264 } 28265 // clipPath 28266 case 'clipPath': { 28267 if ($invisible) { 28268 break; 28269 } 28270 $this->svgclipmode = true; 28271 if (!isset($attribs['id'])) { 28272 $attribs['id'] = 'CP_'.(count($this->svgcliptm) + 1); 28273 } 28274 $this->svgclipid = $attribs['id']; 28275 $this->svgclippaths[$this->svgclipid] = array(); 28276 $this->svgcliptm[$this->svgclipid] = $tm; 28277 break; 28278 } 28279 case 'svg': { 28280 // start of SVG object 28281 break; 28282 } 28283 case 'g': { 28284 // group together related graphics elements 28285 array_push($this->svgstyles, $svgstyle); 28286 $this->StartTransform(); 28287 $this->setSVGStyles($svgstyle, $prev_svgstyle); 28288 break; 28289 } 28290 case 'linearGradient': { 28291 if ($this->pdfa_mode) { 28292 break; 28293 } 28294 if (!isset($attribs['id'])) { 28295 $attribs['id'] = 'GR_'.(count($this->svggradients) + 1); 28296 } 28297 $this->svggradientid = $attribs['id']; 28298 $this->svggradients[$this->svggradientid] = array(); 28299 $this->svggradients[$this->svggradientid]['type'] = 2; 28300 $this->svggradients[$this->svggradientid]['stops'] = array(); 28301 if (isset($attribs['gradientUnits'])) { 28302 $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits']; 28303 } else { 28304 $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox'; 28305 } 28306 //$attribs['spreadMethod'] 28307 $x1 = (isset($attribs['x1'])?$attribs['x1']:'0%'); 28308 $y1 = (isset($attribs['y1'])?$attribs['y1']:'0%'); 28309 $x2 = (isset($attribs['x2'])?$attribs['x2']:'100%'); 28310 $y2 = (isset($attribs['y2'])?$attribs['y2']:'0%'); 28311 if (substr($x1, -1) != '%') { 28312 $this->svggradients[$this->svggradientid]['mode'] = 'measure'; 28313 } else { 28314 $this->svggradients[$this->svggradientid]['mode'] = 'percentage'; 28315 } 28316 if (isset($attribs['gradientTransform'])) { 28317 $this->svggradients[$this->svggradientid]['gradientTransform'] = $this->getSVGTransformMatrix($attribs['gradientTransform']); 28318 } 28319 $this->svggradients[$this->svggradientid]['coords'] = array($x1, $y1, $x2, $y2); 28320 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) { 28321 // gradient is defined on another place 28322 $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1); 28323 } 28324 break; 28325 } 28326 case 'radialGradient': { 28327 if ($this->pdfa_mode) { 28328 break; 28329 } 28330 if (!isset($attribs['id'])) { 28331 $attribs['id'] = 'GR_'.(count($this->svggradients) + 1); 28332 } 28333 $this->svggradientid = $attribs['id']; 28334 $this->svggradients[$this->svggradientid] = array(); 28335 $this->svggradients[$this->svggradientid]['type'] = 3; 28336 $this->svggradients[$this->svggradientid]['stops'] = array(); 28337 if (isset($attribs['gradientUnits'])) { 28338 $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits']; 28339 } else { 28340 $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox'; 28341 } 28342 //$attribs['spreadMethod'] 28343 $cx = (isset($attribs['cx'])?$attribs['cx']:0.5); 28344 $cy = (isset($attribs['cy'])?$attribs['cy']:0.5); 28345 $fx = (isset($attribs['fx'])?$attribs['fx']:$cx); 28346 $fy = (isset($attribs['fy'])?$attribs['fy']:$cy); 28347 $r = (isset($attribs['r'])?$attribs['r']:0.5); 28348 if (isset($attribs['cx']) AND (substr($attribs['cx'], -1) != '%')) { 28349 $this->svggradients[$this->svggradientid]['mode'] = 'measure'; 28350 } else { 28351 $this->svggradients[$this->svggradientid]['mode'] = 'percentage'; 28352 } 28353 if (isset($attribs['gradientTransform'])) { 28354 $this->svggradients[$this->svggradientid]['gradientTransform'] = $this->getSVGTransformMatrix($attribs['gradientTransform']); 28355 } 28356 $this->svggradients[$this->svggradientid]['coords'] = array($cx, $cy, $fx, $fy, $r); 28357 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) { 28358 // gradient is defined on another place 28359 $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1); 28360 } 28361 break; 28362 } 28363 case 'stop': { 28364 // gradient stops 28365 if (substr($attribs['offset'], -1) == '%') { 28366 $offset = floatval(substr($attribs['offset'], -1)) / 100; 28367 } else { 28368 $offset = floatval($attribs['offset']); 28369 if ($offset > 1) { 28370 $offset /= 100; 28371 } 28372 } 28373 $stop_color = isset($svgstyle['stop-color'])?$this->convertHTMLColorToDec($svgstyle['stop-color']):'black'; 28374 $opacity = isset($svgstyle['stop-opacity'])?$svgstyle['stop-opacity']:1; 28375 $this->svggradients[$this->svggradientid]['stops'][] = array('offset' => $offset, 'color' => $stop_color, 'opacity' => $opacity); 28376 break; 28377 } 28378 // paths 28379 case 'path': { 28380 if ($invisible) { 28381 break; 28382 } 28383 if (isset($attribs['d'])) { 28384 $d = trim($attribs['d']); 28385 if (!empty($d)) { 28386 if ($clipping) { 28387 $this->SVGTransform($tm); 28388 $this->SVGPath($d, 'CNZ'); 28389 } else { 28390 $this->StartTransform(); 28391 $this->SVGTransform($tm); 28392 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, 0, 0, 1, 1, 'SVGPath', array($d, 'CNZ')); 28393 if (!empty($obstyle)) { 28394 $this->SVGPath($d, $obstyle); 28395 } 28396 $this->StopTransform(); 28397 } 28398 } 28399 } 28400 break; 28401 } 28402 // shapes 28403 case 'rect': { 28404 if ($invisible) { 28405 break; 28406 } 28407 $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0); 28408 $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0); 28409 $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0); 28410 $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0); 28411 $rx = (isset($attribs['rx'])?$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false):0); 28412 $ry = (isset($attribs['ry'])?$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false):$rx); 28413 if ($clipping) { 28414 $this->SVGTransform($tm); 28415 $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ', array(), array()); 28416 } else { 28417 $this->StartTransform(); 28418 $this->SVGTransform($tm); 28419 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'RoundedRectXY', array($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ')); 28420 if (!empty($obstyle)) { 28421 $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', $obstyle, array(), array()); 28422 } 28423 $this->StopTransform(); 28424 } 28425 break; 28426 } 28427 case 'circle': { 28428 if ($invisible) { 28429 break; 28430 } 28431 $cx = (isset($attribs['cx'])?$this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false):0); 28432 $cy = (isset($attribs['cy'])?$this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false):0); 28433 $r = (isset($attribs['r'])?$this->getHTMLUnitToUnits($attribs['r'], 0, $this->svgunit, false):0); 28434 $x = $cx - $r; 28435 $y = $cy - $r; 28436 $w = 2 * $r; 28437 $h = $w; 28438 if ($clipping) { 28439 $this->SVGTransform($tm); 28440 $this->Circle($cx, $cy, $r, 0, 360, 'CNZ', array(), array(), 8); 28441 } else { 28442 $this->StartTransform(); 28443 $this->SVGTransform($tm); 28444 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Circle', array($cx, $cy, $r, 0, 360, 'CNZ')); 28445 if (!empty($obstyle)) { 28446 $this->Circle($cx, $cy, $r, 0, 360, $obstyle, array(), array(), 8); 28447 } 28448 $this->StopTransform(); 28449 } 28450 break; 28451 } 28452 case 'ellipse': { 28453 if ($invisible) { 28454 break; 28455 } 28456 $cx = (isset($attribs['cx'])?$this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false):0); 28457 $cy = (isset($attribs['cy'])?$this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false):0); 28458 $rx = (isset($attribs['rx'])?$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false):0); 28459 $ry = (isset($attribs['ry'])?$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false):0); 28460 $x = $cx - $rx; 28461 $y = $cy - $ry; 28462 $w = 2 * $rx; 28463 $h = 2 * $ry; 28464 if ($clipping) { 28465 $this->SVGTransform($tm); 28466 $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ', array(), array(), 8); 28467 } else { 28468 $this->StartTransform(); 28469 $this->SVGTransform($tm); 28470 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Ellipse', array($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ')); 28471 if (!empty($obstyle)) { 28472 $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, $obstyle, array(), array(), 8); 28473 } 28474 $this->StopTransform(); 28475 } 28476 break; 28477 } 28478 case 'line': { 28479 if ($invisible) { 28480 break; 28481 } 28482 $x1 = (isset($attribs['x1'])?$this->getHTMLUnitToUnits($attribs['x1'], 0, $this->svgunit, false):0); 28483 $y1 = (isset($attribs['y1'])?$this->getHTMLUnitToUnits($attribs['y1'], 0, $this->svgunit, false):0); 28484 $x2 = (isset($attribs['x2'])?$this->getHTMLUnitToUnits($attribs['x2'], 0, $this->svgunit, false):0); 28485 $y2 = (isset($attribs['y2'])?$this->getHTMLUnitToUnits($attribs['y2'], 0, $this->svgunit, false):0); 28486 $x = $x1; 28487 $y = $y1; 28488 $w = abs($x2 - $x1); 28489 $h = abs($y2 - $y1); 28490 if (!$clipping) { 28491 $this->StartTransform(); 28492 $this->SVGTransform($tm); 28493 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Line', array($x1, $y1, $x2, $y2)); 28494 $this->Line($x1, $y1, $x2, $y2); 28495 $this->StopTransform(); 28496 } 28497 break; 28498 } 28499 case 'polyline': 28500 case 'polygon': { 28501 if ($invisible) { 28502 break; 28503 } 28504 $points = (isset($attribs['points'])?$attribs['points']:'0 0'); 28505 $points = trim($points); 28506 // note that point may use a complex syntax not covered here 28507 $points = preg_split('/[\,\s]+/si', $points); 28508 if (count($points) < 4) { 28509 break; 28510 } 28511 $p = array(); 28512 $xmin = 2147483647; 28513 $xmax = 0; 28514 $ymin = 2147483647; 28515 $ymax = 0; 28516 foreach ($points as $key => $val) { 28517 $p[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false); 28518 if (($key % 2) == 0) { 28519 // X coordinate 28520 $xmin = min($xmin, $p[$key]); 28521 $xmax = max($xmax, $p[$key]); 28522 } else { 28523 // Y coordinate 28524 $ymin = min($ymin, $p[$key]); 28525 $ymax = max($ymax, $p[$key]); 28526 } 28527 } 28528 $x = $xmin; 28529 $y = $ymin; 28530 $w = ($xmax - $xmin); 28531 $h = ($ymax - $ymin); 28532 if ($name == 'polyline') { 28533 $this->StartTransform(); 28534 $this->SVGTransform($tm); 28535 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'PolyLine', array($p, 'CNZ')); 28536 $this->PolyLine($p, 'D', array(), array()); 28537 $this->StopTransform(); 28538 } else { // polygon 28539 if ($clipping) { 28540 $this->SVGTransform($tm); 28541 $this->Polygon($p, 'CNZ', array(), array(), true); 28542 } else { 28543 $this->StartTransform(); 28544 $this->SVGTransform($tm); 28545 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Polygon', array($p, 'CNZ')); 28546 if (!empty($obstyle)) { 28547 $this->Polygon($p, $obstyle, array(), array(), true); 28548 } 28549 $this->StopTransform(); 28550 } 28551 } 28552 break; 28553 } 28554 // image 28555 case 'image': { 28556 if ($invisible) { 28557 break; 28558 } 28559 if (!isset($attribs['xlink:href']) OR empty($attribs['xlink:href'])) { 28560 break; 28561 } 28562 $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0); 28563 $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0); 28564 $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0); 28565 $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0); 28566 $img = $attribs['xlink:href']; 28567 if (!$clipping) { 28568 $this->StartTransform(); 28569 $this->SVGTransform($tm); 28570 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h); 28571 // fix image path 28572 if (!$this->empty_string($this->svgdir) AND (($img{0} == '.') OR (basename($img) == $img))) { 28573 // replace relative path with full server path 28574 $img = $this->svgdir.'/'.$img; 28575 } 28576 if (($img[0] == '/') AND !empty($_SERVER['DOCUMENT_ROOT']) AND ($_SERVER['DOCUMENT_ROOT'] != '/')) { 28577 $findroot = strpos($img, $_SERVER['DOCUMENT_ROOT']); 28578 if (($findroot === false) OR ($findroot > 1)) { 28579 if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') { 28580 $img = substr($_SERVER['DOCUMENT_ROOT'], 0, -1).$img; 28581 } else { 28582 $img = $_SERVER['DOCUMENT_ROOT'].$img; 28583 } 28584 } 28585 } 28586 $img = urldecode($img); 28587 $testscrtype = @parse_url($img); 28588 if (!isset($testscrtype['query']) OR empty($testscrtype['query'])) { 28589 // convert URL to server path 28590 $img = str_replace(K_PATH_URL, K_PATH_MAIN, $img); 28591 } 28592 $this->Image($img, $x, $y, $w, $h); 28593 $this->StopTransform(); 28594 } 28595 break; 28596 } 28597 // text 28598 case 'text': 28599 case 'tspan': { 28600 $this->svgtextmode['invisible'] = $invisible; 28601 if ($invisible) { 28602 break; 28603 } 28604 array_push($this->svgstyles, $svgstyle); 28605 // only basic support - advanced features must be implemented 28606 $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):$this->x); 28607 $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):$this->y); 28608 $svgstyle['text-color'] = $svgstyle['fill']; 28609 $this->svgtext = ''; 28610 if (isset($svgstyle['text-anchor'])) { 28611 $this->svgtextmode['text-anchor'] = $svgstyle['text-anchor']; 28612 } else { 28613 $this->svgtextmode['text-anchor'] = 'start'; 28614 } 28615 if (isset($svgstyle['direction'])) { 28616 if ($svgstyle['direction'] == 'rtl') { 28617 $this->svgtextmode['rtl'] = true; 28618 } else { 28619 $this->svgtextmode['rtl'] = false; 28620 } 28621 } else { 28622 $this->svgtextmode['rtl'] = false; 28623 } 28624 if (isset($svgstyle['stroke']) AND ($svgstyle['stroke'] != 'none') AND isset($svgstyle['stroke-width']) AND ($svgstyle['stroke-width'] > 0)) { 28625 $this->svgtextmode['stroke'] = $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false); 28626 } else { 28627 $this->svgtextmode['stroke'] = false; 28628 } 28629 $this->StartTransform(); 28630 $this->SVGTransform($tm); 28631 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, 1, 1); 28632 $this->x = $x; 28633 $this->y = $y; 28634 break; 28635 } 28636 // use 28637 case 'use': { 28638 if (isset($attribs['xlink:href'])) { 28639 $use = $this->svgdefs[substr($attribs['xlink:href'], 1)]; 28640 if (isset($attribs['xlink:href'])) { 28641 unset($attribs['xlink:href']); 28642 } 28643 if (isset($attribs['id'])) { 28644 unset($attribs['id']); 28645 } 28646 $attribs = array_merge($use['attribs'], $attribs); 28647 $this->startSVGElementHandler($parser, $use['name'], $use['attribs']); 28648 } 28649 break; 28650 } 28651 default: { 28652 break; 28653 } 28654 } // end of switch 28655 } 28656 28665 protected function endSVGElementHandler($parser, $name) { 28666 switch($name) { 28667 case 'defs': { 28668 $this->svgdefsmode = false; 28669 break; 28670 } 28671 // clipPath 28672 case 'clipPath': { 28673 $this->svgclipmode = false; 28674 break; 28675 } 28676 case 'g': { 28677 // ungroup: remove last style from array 28678 array_pop($this->svgstyles); 28679 $this->StopTransform(); 28680 break; 28681 } 28682 case 'text': 28683 case 'tspan': { 28684 if ($this->svgtextmode['invisible']) { 28685 // This implementation must be fixed to following the rule: 28686 // If the 'visibility' property is set to hidden on a 'tspan', 'tref' or 'altGlyph' element, then the text is invisible but still takes up space in text layout calculations. 28687 break; 28688 } 28689 // print text 28690 $text = $this->stringTrim($this->svgtext); 28691 if ($this->svgtextmode['text-anchor'] != 'start') { 28692 $textlen = $this->GetStringWidth($text); 28693 // check if string is RTL text 28694 if ($this->svgtextmode['text-anchor'] == 'end') { 28695 if ($this->svgtextmode['rtl']) { 28696 $this->x += $textlen; 28697 } else { 28698 $this->x -= $textlen; 28699 } 28700 } elseif ($this->svgtextmode['text-anchor'] == 'middle') { 28701 if ($this->svgtextmode['rtl']) { 28702 $this->x += ($textlen / 2); 28703 } else { 28704 $this->x -= ($textlen / 2); 28705 } 28706 } 28707 } 28708 $textrendermode = $this->textrendermode; 28709 $textstrokewidth = $this->textstrokewidth; 28710 $this->setTextRenderingMode($this->svgtextmode['stroke'], true, false); 28711 $this->Cell(0, 0, $text, 0, 0, '', false, '', 0, false, 'L', 'T'); 28712 // restore previous rendering mode 28713 $this->textrendermode = $textrendermode; 28714 $this->textstrokewidth = $textstrokewidth; 28715 $this->svgtext = ''; 28716 $this->StopTransform(); 28717 array_pop($this->svgstyles); 28718 break; 28719 } 28720 default: { 28721 break; 28722 } 28723 } 28724 } 28725 28734 protected function segSVGContentHandler($parser, $data) { 28735 $this->svgtext .= $data; 28736 } 28737 28738 // --- END SVG METHODS ----------------------------------------------------- 28739 28740 } // END OF TCPDF CLASS 28741 28742 //============================================================+ 28743 // END OF FILE 28744 //============================================================+