[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 <?php 2 //============================================================+ 3 // File name : tcpdf.php 4 // Begin : 2002-08-03 5 // Last Update : 2010-05-06 6 // Author : Nicola Asuni - info@tecnick.com - http://www.tcpdf.org 7 // Version : 5.0.002 8 // License : GNU LGPL (http://www.gnu.org/copyleft/lesser.html) 9 // ---------------------------------------------------------------------------- 10 // Copyright (C) 2002-2010 Nicola Asuni - Tecnick.com S.r.l. 11 // 12 // This program is free software: you can redistribute it and/or modify 13 // it under the terms of the GNU Lesser General Public License as published by 14 // the Free Software Foundation, either version 2.1 of the License, or 15 // (at your option) any later version. 16 // 17 // This program is distributed in the hope that it will be useful, 18 // but WITHOUT ANY WARRANTY; without even the implied warranty of 19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 // GNU Lesser General Public License for more details. 21 // 22 // You should have received a copy of the GNU Lesser General Public License 23 // along with this program. If not, see <http://www.gnu.org/licenses/>. 24 // 25 // See LICENSE.TXT file for more information. 26 // ---------------------------------------------------------------------------- 27 // 28 // Description : This is a PHP class for generating PDF documents without 29 // requiring external extensions. 30 // 31 // NOTE: 32 // This class was originally derived in 2002 from the Public 33 // Domain FPDF class by Olivier Plathey (http://www.fpdf.org), 34 // but now is almost entirely rewritten. 35 // 36 // Main features: 37 // * no external libraries are required for the basic functions; 38 // * all ISO page formats, custom page formats, custom margins and units of measure; 39 // * UTF-8 Unicode and Right-To-Left languages; 40 // * TrueTypeUnicode, OpenTypeUnicode, TrueType, OpenType, Type1 and CID-0 fonts; 41 // * methods to publish some XHTML code, Javascript and Forms; 42 // * images, graphic (geometric figures) and transformation methods; 43 // * 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) 44 // * 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, QR-Code; 45 // * Grayscale, RGB, CMYK, Spot Colors and Transparencies; 46 // * automatic page header and footer management; 47 // * document encryption and digital signature certifications; 48 // * transactions to UNDO commands; 49 // * PDF annotations, including links, text and file attachments; 50 // * text rendering modes (fill, stroke and clipping); 51 // * multiple columns mode; 52 // * bookmarks and table of content; 53 // * text hyphenation; 54 // * automatic page break, line break and text alignments including justification; 55 // * automatic page numbering and page groups; 56 // * move and delete pages; 57 // * page compression (requires php-zlib extension); 58 // 59 // ----------------------------------------------------------- 60 // THANKS TO: 61 // 62 // Olivier Plathey (http://www.fpdf.org) for original FPDF. 63 // Efthimios Mavrogeorgiadis (emavro@yahoo.com) for suggestions on RTL language support. 64 // Klemen Vodopivec (http://www.fpdf.de/downloads/addons/37/) for Encryption algorithm. 65 // Warren Sherliker (wsherliker@gmail.com) for better image handling. 66 // dullus for text Justification. 67 // Bob Vincent (pillarsdotnet@users.sourceforge.net) for <li> value attribute. 68 // Patrick Benny for text stretch suggestion on Cell(). 69 // Johannes Güntert for JavaScript support. 70 // Denis Van Nuffelen for Dynamic Form. 71 // Jacek Czekaj for multibyte justification 72 // Anthony Ferrara for the reintroduction of legacy image methods. 73 // Sourceforge user 1707880 (hucste) for line-trough mode. 74 // Larry Stanbery for page groups. 75 // Martin Hall-May for transparency. 76 // Aaron C. Spike for Polycurve method. 77 // Mohamad Ali Golkar, Saleh AlMatrafe, Charles Abbott for Arabic and Persian support. 78 // Moritz Wagner and Andreas Wurmser for graphic functions. 79 // Andrew Whitehead for core fonts support. 80 // Esteban Joël Marín for OpenType font conversion. 81 // Teus Hagen for several suggestions and fixes. 82 // Yukihiro Nakadaira for CID-0 CJK fonts fixes. 83 // Kosmas Papachristos for some CSS improvements. 84 // Marcel Partap for some fixes. 85 // Won Kyu Park for several suggestions, fixes and patches. 86 // Dominik Dzienia for QR-code support. 87 // Laurent Minguet for some suggestions. 88 // Anyone that has reported a bug or sent a suggestion. 89 //============================================================+ 90 91 /** 92 * This is a PHP class for generating PDF documents without requiring external extensions.<br> 93 * TCPDF project (http://www.tcpdf.org) was originally derived in 2002 from the Public Domain FPDF class by Olivier Plathey (http://www.fpdf.org), but now is almost entirely rewritten.<br> 94 * <h3>TCPDF main features are:</h3> 95 * <ul> 96 * <li>no external libraries are required for the basic functions;</li> 97 * <li>all ISO page formats, custom page formats, custom margins and units of measure;</li> 98 * <li>UTF-8 Unicode and Right-To-Left languages;</li> 99 * <li>TrueTypeUnicode, OpenTypeUnicode, TrueType, OpenType, Type1 and CID-0 fonts;</li> 100 * <li>methods to publish some XHTML code, Javascript and Forms;</li> 101 * <li>images, graphic (geometric figures) and transformation methods; 102 * <li>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)</li> 103 * <li>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, QR-Code;</li> 104 * <li>Grayscale, RGB, CMYK, Spot Colors and Transparencies;</li> 105 * <li>automatic page header and footer management;</li> 106 * <li>document encryption and digital signature certifications;</li> 107 * <li>transactions to UNDO commands;</li> 108 * <li>PDF annotations, including links, text and file attachments;</li> 109 * <li>text rendering modes (fill, stroke and clipping);</li> 110 * <li>multiple columns mode;</li> 111 * <li>bookmarks and table of content;</li> 112 * <li>text hyphenation;</li> 113 * <li>automatic page break, line break and text alignments including justification;</li> 114 * <li>automatic page numbering and page groups;</li> 115 * <li>move and delete pages;</li> 116 * <li>page compression (requires php-zlib extension);</li> 117 * </ul> 118 * Tools to encode your unicode fonts are on fonts/utils directory.</p> 119 * @package com.tecnick.tcpdf 120 * @abstract Class for generating PDF files on-the-fly without requiring external extensions. 121 * @author Nicola Asuni 122 * @copyright 2002-2010 Nicola Asuni - Tecnick.com S.r.l (www.tecnick.com) Via Della Pace, 11 - 09044 - Quartucciu (CA) - ITALY - www.tecnick.com - info@tecnick.com 123 * @link http://www.tcpdf.org 124 * @license http://www.gnu.org/copyleft/lesser.html LGPL 125 * @version 5.0.002 126 */ 127 128 /** 129 * main configuration file 130 */ 131 require_once(dirname(__FILE__).'/config/tcpdf_config.php'); 132 133 // includes some support files 134 135 /** 136 * unicode data 137 */ 138 require_once(dirname(__FILE__).'/unicode_data.php'); 139 140 /** 141 * html colors table 142 */ 143 require_once(dirname(__FILE__).'/htmlcolors.php'); 144 145 if (!class_exists('TCPDF', false)) { 146 /** 147 * define default PDF document producer 148 */ 149 define('PDF_PRODUCER', 'TCPDF 5.0.002 (http://www.tcpdf.org)'); 150 151 /** 152 * This is a PHP class for generating PDF documents without requiring external extensions.<br> 153 * TCPDF project (http://www.tcpdf.org) has been originally derived in 2002 from the Public Domain FPDF class by Olivier Plathey (http://www.fpdf.org), but now is almost entirely rewritten.<br> 154 * @name TCPDF 155 * @package com.tecnick.tcpdf 156 * @version 5.0.002 157 * @author Nicola Asuni - info@tecnick.com 158 * @link http://www.tcpdf.org 159 * @license http://www.gnu.org/copyleft/lesser.html LGPL 160 */ 161 class TCPDF { 162 163 // protected or Protected properties 164 165 /** 166 * @var current page number 167 * @access protected 168 */ 169 protected $page; 170 171 /** 172 * @var current object number 173 * @access protected 174 */ 175 protected $n; 176 177 /** 178 * @var array of object offsets 179 * @access protected 180 */ 181 protected $offsets; 182 183 /** 184 * @var buffer holding in-memory PDF 185 * @access protected 186 */ 187 protected $buffer; 188 189 /** 190 * @var array containing pages 191 * @access protected 192 */ 193 protected $pages = array(); 194 195 /** 196 * @var current document state 197 * @access protected 198 */ 199 protected $state; 200 201 /** 202 * @var compression flag 203 * @access protected 204 */ 205 protected $compress; 206 207 /** 208 * @var current page orientation (P = Portrait, L = Landscape) 209 * @access protected 210 */ 211 protected $CurOrientation; 212 213 /** 214 * @var array that stores page dimensions and graphic status.<ul><li>$this->pagedim[$this->page]['w'] => page_width_in_points</li><li>$this->pagedim[$this->page]['h'] => height in points</li><li>$this->pagedim[$this->page]['wk'] => page_width_in_points</li><li>$this->pagedim[$this->page]['hk'] => height</li><li>$this->pagedim[$this->page]['tm'] => top_margin</li><li>$this->pagedim[$this->page]['bm'] => bottom_margin</li><li>$this->pagedim[$this->page]['lm'] => left_margin</li><li>$this->pagedim[$this->page]['rm'] => right_margin</li><li>$this->pagedim[$this->page]['pb'] => auto_page_break</li><li>$this->pagedim[$this->page]['or'] => page_orientation</li><li>$this->pagedim[$this->page]['olm'] => original_left_margin</li><li>$this->pagedim[$this->page]['orm'] => original_right_margin</li></ul> 215 * @access protected 216 */ 217 protected $pagedim = array(); 218 219 /** 220 * @var scale factor (number of points in user unit) 221 * @access protected 222 */ 223 protected $k; 224 225 /** 226 * @var width of page format in points 227 * @access protected 228 */ 229 protected $fwPt; 230 231 /** 232 * @var height of page format in points 233 * @access protected 234 */ 235 protected $fhPt; 236 237 /** 238 * @var current width of page in points 239 * @access protected 240 */ 241 protected $wPt; 242 243 /** 244 * @var current height of page in points 245 * @access protected 246 */ 247 protected $hPt; 248 249 /** 250 * @var current width of page in user unit 251 * @access protected 252 */ 253 protected $w; 254 255 /** 256 * @var current height of page in user unit 257 * @access protected 258 */ 259 protected $h; 260 261 /** 262 * @var left margin 263 * @access protected 264 */ 265 protected $lMargin; 266 267 /** 268 * @var top margin 269 * @access protected 270 */ 271 protected $tMargin; 272 273 /** 274 * @var right margin 275 * @access protected 276 */ 277 protected $rMargin; 278 279 /** 280 * @var page break margin 281 * @access protected 282 */ 283 protected $bMargin; 284 285 /** 286 * @var cell internal padding 287 * @access protected 288 */ 289 //protected 290 public $cMargin; 291 292 /** 293 * @var cell internal padding (previous value) 294 * @access protected 295 */ 296 protected $oldcMargin; 297 298 /** 299 * @var current horizontal position in user unit for cell positioning 300 * @access protected 301 */ 302 protected $x; 303 304 /** 305 * @var current vertical position in user unit for cell positioning 306 * @access protected 307 */ 308 protected $y; 309 310 /** 311 * @var height of last cell printed 312 * @access protected 313 */ 314 protected $lasth; 315 316 /** 317 * @var line width in user unit 318 * @access protected 319 */ 320 protected $LineWidth; 321 322 /** 323 * @var array of standard font names 324 * @access protected 325 */ 326 protected $CoreFonts; 327 328 /** 329 * @var array of used fonts 330 * @access protected 331 */ 332 protected $fonts = array(); 333 334 /** 335 * @var array of font files 336 * @access protected 337 */ 338 protected $FontFiles = array(); 339 340 /** 341 * @var array of encoding differences 342 * @access protected 343 */ 344 protected $diffs = array(); 345 346 /** 347 * @var array of used images 348 * @access protected 349 */ 350 protected $images = array(); 351 352 /** 353 * @var array of Annotations in pages 354 * @access protected 355 */ 356 protected $PageAnnots = array(); 357 358 /** 359 * @var array of internal links 360 * @access protected 361 */ 362 protected $links = array(); 363 364 /** 365 * @var current font family 366 * @access protected 367 */ 368 protected $FontFamily; 369 370 /** 371 * @var current font style 372 * @access protected 373 */ 374 protected $FontStyle; 375 376 /** 377 * @var current font ascent (distance between font top and baseline) 378 * @access protected 379 * @since 2.8.000 (2007-03-29) 380 */ 381 protected $FontAscent; 382 383 /** 384 * @var current font descent (distance between font bottom and baseline) 385 * @access protected 386 * @since 2.8.000 (2007-03-29) 387 */ 388 protected $FontDescent; 389 390 /** 391 * @var underlining flag 392 * @access protected 393 */ 394 protected $underline; 395 396 /** 397 * @var overlining flag 398 * @access protected 399 */ 400 protected $overline; 401 402 /** 403 * @var current font info 404 * @access protected 405 */ 406 protected $CurrentFont; 407 408 /** 409 * @var current font size in points 410 * @access protected 411 */ 412 protected $FontSizePt; 413 414 /** 415 * @var current font size in user unit 416 * @access protected 417 */ 418 protected $FontSize; 419 420 /** 421 * @var commands for drawing color 422 * @access protected 423 */ 424 protected $DrawColor; 425 426 /** 427 * @var commands for filling color 428 * @access protected 429 */ 430 protected $FillColor; 431 432 /** 433 * @var commands for text color 434 * @access protected 435 */ 436 protected $TextColor; 437 438 /** 439 * @var indicates whether fill and text colors are different 440 * @access protected 441 */ 442 protected $ColorFlag; 443 444 /** 445 * @var automatic page breaking 446 * @access protected 447 */ 448 protected $AutoPageBreak; 449 450 /** 451 * @var threshold used to trigger page breaks 452 * @access protected 453 */ 454 protected $PageBreakTrigger; 455 456 /** 457 * @var flag set when processing footer 458 * @access protected 459 */ 460 protected $InFooter = false; 461 462 /** 463 * @var zoom display mode 464 * @access protected 465 */ 466 protected $ZoomMode; 467 468 /** 469 * @var layout display mode 470 * @access protected 471 */ 472 protected $LayoutMode; 473 474 /** 475 * @var title 476 * @access protected 477 */ 478 protected $title = ''; 479 480 /** 481 * @var subject 482 * @access protected 483 */ 484 protected $subject = ''; 485 486 /** 487 * @var author 488 * @access protected 489 */ 490 protected $author = ''; 491 492 /** 493 * @var keywords 494 * @access protected 495 */ 496 protected $keywords = ''; 497 498 /** 499 * @var creator 500 * @access protected 501 */ 502 protected $creator = ''; 503 504 /** 505 * @var alias for total number of pages 506 * @access protected 507 */ 508 protected $AliasNbPages = '{nb}'; 509 510 /** 511 * @var alias for page number 512 * @access protected 513 */ 514 protected $AliasNumPage = '{pnb}'; 515 516 /** 517 * @var right-bottom corner X coordinate of inserted image 518 * @since 2002-07-31 519 * @author Nicola Asuni 520 * @access protected 521 */ 522 protected $img_rb_x; 523 524 /** 525 * @var right-bottom corner Y coordinate of inserted image 526 * @since 2002-07-31 527 * @author Nicola Asuni 528 * @access protected 529 */ 530 protected $img_rb_y; 531 532 /** 533 * @var adjusting factor to convert pixels to user units. 534 * @since 2004-06-14 535 * @author Nicola Asuni 536 * @access protected 537 */ 538 protected $imgscale = 1; 539 540 /** 541 * @var boolean set to true when the input text is unicode (require unicode fonts) 542 * @since 2005-01-02 543 * @author Nicola Asuni 544 * @access protected 545 */ 546 protected $isunicode = false; 547 548 /** 549 * @var PDF version 550 * @since 1.5.3 551 * @access protected 552 */ 553 protected $PDFVersion = '1.7'; 554 555 /** 556 * @var Minimum distance between header and top page margin. 557 * @access protected 558 */ 559 protected $header_margin; 560 561 /** 562 * @var Minimum distance between footer and bottom page margin. 563 * @access protected 564 */ 565 protected $footer_margin; 566 567 /** 568 * @var original left margin value 569 * @access protected 570 * @since 1.53.0.TC013 571 */ 572 protected $original_lMargin; 573 574 /** 575 * @var original right margin value 576 * @access protected 577 * @since 1.53.0.TC013 578 */ 579 protected $original_rMargin; 580 581 /** 582 * @var Header font. 583 * @access protected 584 */ 585 protected $header_font; 586 587 /** 588 * @var Footer font. 589 * @access protected 590 */ 591 protected $footer_font; 592 593 /** 594 * @var Language templates. 595 * @access protected 596 */ 597 protected $l; 598 599 /** 600 * @var Barcode to print on page footer (only if set). 601 * @access protected 602 */ 603 protected $barcode = false; 604 605 /** 606 * @var If true prints header 607 * @access protected 608 */ 609 protected $print_header = true; 610 611 /** 612 * @var If true prints footer. 613 * @access protected 614 */ 615 protected $print_footer = true; 616 617 /** 618 * @var Header image logo. 619 * @access protected 620 */ 621 protected $header_logo = ''; 622 623 /** 624 * @var Header image logo width in mm. 625 * @access protected 626 */ 627 protected $header_logo_width = 30; 628 629 /** 630 * @var String to print as title on document header. 631 * @access protected 632 */ 633 protected $header_title = ''; 634 635 /** 636 * @var String to print on document header. 637 * @access protected 638 */ 639 protected $header_string = ''; 640 641 /** 642 * @var Default number of columns for html table. 643 * @access protected 644 */ 645 protected $default_table_columns = 4; 646 647 648 // variables for html parser 649 650 /** 651 * @var HTML PARSER: array to store current link and rendering styles. 652 * @access protected 653 */ 654 protected $HREF = array(); 655 656 /** 657 * @var store a list of available fonts on filesystem. 658 * @access protected 659 */ 660 protected $fontlist = array(); 661 662 /** 663 * @var current foreground color 664 * @access protected 665 */ 666 protected $fgcolor; 667 668 /** 669 * @var HTML PARSER: array of boolean values, true in case of ordered list (OL), false otherwise. 670 * @access protected 671 */ 672 protected $listordered = array(); 673 674 /** 675 * @var HTML PARSER: array count list items on nested lists. 676 * @access protected 677 */ 678 protected $listcount = array(); 679 680 /** 681 * @var HTML PARSER: current list nesting level. 682 * @access protected 683 */ 684 protected $listnum = 0; 685 686 /** 687 * @var HTML PARSER: indent amount for lists. 688 * @access protected 689 */ 690 protected $listindent = 0; 691 692 /** 693 * @var HTML PARSER: current list indententation level. 694 * @access protected 695 */ 696 protected $listindentlevel = 0; 697 698 /** 699 * @var current background color 700 * @access protected 701 */ 702 protected $bgcolor; 703 704 /** 705 * @var Store temporary font size in points. 706 * @access protected 707 */ 708 protected $tempfontsize = 10; 709 710 /** 711 * @var spacer for LI tags. 712 * @access protected 713 */ 714 protected $lispacer = ''; 715 716 /** 717 * @var default encoding 718 * @access protected 719 * @since 1.53.0.TC010 720 */ 721 protected $encoding = 'UTF-8'; 722 723 /** 724 * @var PHP internal encoding 725 * @access protected 726 * @since 1.53.0.TC016 727 */ 728 protected $internal_encoding; 729 730 /** 731 * @var indicates if the document language is Right-To-Left 732 * @access protected 733 * @since 2.0.000 734 */ 735 protected $rtl = false; 736 737 /** 738 * @var used to force RTL or LTR string inversion 739 * @access protected 740 * @since 2.0.000 741 */ 742 protected $tmprtl = false; 743 744 // --- Variables used for document encryption: 745 746 /** 747 * Indicates whether document is protected 748 * @access protected 749 * @since 2.0.000 (2008-01-02) 750 */ 751 protected $encrypted; 752 753 /** 754 * U entry in pdf document 755 * @access protected 756 * @since 2.0.000 (2008-01-02) 757 */ 758 protected $Uvalue; 759 760 /** 761 * O entry in pdf document 762 * @access protected 763 * @since 2.0.000 (2008-01-02) 764 */ 765 protected $Ovalue; 766 767 /** 768 * P entry in pdf document 769 * @access protected 770 * @since 2.0.000 (2008-01-02) 771 */ 772 protected $Pvalue; 773 774 /** 775 * encryption object id 776 * @access protected 777 * @since 2.0.000 (2008-01-02) 778 */ 779 protected $enc_obj_id; 780 781 /** 782 * last RC4 key encrypted (cached for optimisation) 783 * @access protected 784 * @since 2.0.000 (2008-01-02) 785 */ 786 protected $last_rc4_key; 787 788 /** 789 * last RC4 computed key 790 * @access protected 791 * @since 2.0.000 (2008-01-02) 792 */ 793 protected $last_rc4_key_c; 794 795 /** 796 * RC4 padding 797 * @access protected 798 */ 799 protected $padding = "\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A"; 800 801 /** 802 * RC4 encryption key 803 * @access protected 804 */ 805 protected $encryption_key; 806 807 // --- bookmark --- 808 809 /** 810 * Outlines for bookmark 811 * @access protected 812 * @since 2.1.002 (2008-02-12) 813 */ 814 protected $outlines = array(); 815 816 /** 817 * Outline root for bookmark 818 * @access protected 819 * @since 2.1.002 (2008-02-12) 820 */ 821 protected $OutlineRoot; 822 823 824 // --- javascript and form --- 825 826 /** 827 * javascript code 828 * @access protected 829 * @since 2.1.002 (2008-02-12) 830 */ 831 protected $javascript = ''; 832 833 /** 834 * javascript counter 835 * @access protected 836 * @since 2.1.002 (2008-02-12) 837 */ 838 protected $n_js; 839 840 /** 841 * line trough state 842 * @access protected 843 * @since 2.8.000 (2008-03-19) 844 */ 845 protected $linethrough; 846 847 // --- Variables used for User's Rights --- 848 // See PDF reference chapter 8.7 Digital Signatures 849 850 /** 851 * If true enables user's rights on PDF reader 852 * @access protected 853 * @since 2.9.000 (2008-03-26) 854 */ 855 protected $ur; 856 857 /** 858 * Names specifying additional document-wide usage rights for the document. 859 * @access protected 860 * @since 2.9.000 (2008-03-26) 861 */ 862 protected $ur_document; 863 864 /** 865 * Names specifying additional annotation-related usage rights for the document. 866 * @access protected 867 * @since 2.9.000 (2008-03-26) 868 */ 869 protected $ur_annots; 870 871 /** 872 * Names specifying additional form-field-related usage rights for the document. 873 * @access protected 874 * @since 2.9.000 (2008-03-26) 875 */ 876 protected $ur_form; 877 878 /** 879 * Names specifying additional signature-related usage rights for the document. 880 * @access protected 881 * @since 2.9.000 (2008-03-26) 882 */ 883 protected $ur_signature; 884 885 /** 886 * Dot Per Inch Document Resolution (do not change) 887 * @access protected 888 * @since 3.0.000 (2008-03-27) 889 */ 890 protected $dpi = 72; 891 892 /** 893 * Array of page numbers were a new page group was started 894 * @access protected 895 * @since 3.0.000 (2008-03-27) 896 */ 897 protected $newpagegroup = array(); 898 899 /** 900 * Contains the number of pages of the groups 901 * @access protected 902 * @since 3.0.000 (2008-03-27) 903 */ 904 protected $pagegroups; 905 906 /** 907 * Contains the alias of the current page group 908 * @access protected 909 * @since 3.0.000 (2008-03-27) 910 */ 911 protected $currpagegroup; 912 913 /** 914 * Restrict the rendering of some elements to screen or printout. 915 * @access protected 916 * @since 3.0.000 (2008-03-27) 917 */ 918 protected $visibility = 'all'; 919 920 /** 921 * Print visibility. 922 * @access protected 923 * @since 3.0.000 (2008-03-27) 924 */ 925 protected $n_ocg_print; 926 927 /** 928 * View visibility. 929 * @access protected 930 * @since 3.0.000 (2008-03-27) 931 */ 932 protected $n_ocg_view; 933 934 /** 935 * Array of transparency objects and parameters. 936 * @access protected 937 * @since 3.0.000 (2008-03-27) 938 */ 939 protected $extgstates; 940 941 /** 942 * Set the default JPEG compression quality (1-100) 943 * @access protected 944 * @since 3.0.000 (2008-03-27) 945 */ 946 protected $jpeg_quality; 947 948 /** 949 * Default cell height ratio. 950 * @access protected 951 * @since 3.0.014 (2008-05-23) 952 */ 953 protected $cell_height_ratio = K_CELL_HEIGHT_RATIO; 954 955 /** 956 * PDF viewer preferences. 957 * @access protected 958 * @since 3.1.000 (2008-06-09) 959 */ 960 protected $viewer_preferences; 961 962 /** 963 * A name object specifying how the document should be displayed when opened. 964 * @access protected 965 * @since 3.1.000 (2008-06-09) 966 */ 967 protected $PageMode; 968 969 /** 970 * Array for storing gradient information. 971 * @access protected 972 * @since 3.1.000 (2008-06-09) 973 */ 974 protected $gradients = array(); 975 976 /** 977 * Array used to store positions inside the pages buffer. 978 * keys are the page numbers 979 * @access protected 980 * @since 3.2.000 (2008-06-26) 981 */ 982 protected $intmrk = array(); 983 984 /** 985 * Array used to store content positions inside the pages buffer. 986 * keys are the page numbers 987 * @access protected 988 * @since 4.6.021 (2009-07-20) 989 */ 990 protected $cntmrk = array(); 991 992 /** 993 * Array used to store footer positions of each page. 994 * @access protected 995 * @since 3.2.000 (2008-07-01) 996 */ 997 protected $footerpos = array(); 998 999 1000 /** 1001 * Array used to store footer length of each page. 1002 * @access protected 1003 * @since 4.0.014 (2008-07-29) 1004 */ 1005 protected $footerlen = array(); 1006 1007 /** 1008 * True if a newline is created. 1009 * @access protected 1010 * @since 3.2.000 (2008-07-01) 1011 */ 1012 protected $newline = true; 1013 1014 /** 1015 * End position of the latest inserted line 1016 * @access protected 1017 * @since 3.2.000 (2008-07-01) 1018 */ 1019 protected $endlinex = 0; 1020 1021 /** 1022 * PDF string for last line width 1023 * @access protected 1024 * @since 4.0.006 (2008-07-16) 1025 */ 1026 protected $linestyleWidth = ''; 1027 1028 /** 1029 * PDF string for last line width 1030 * @access protected 1031 * @since 4.0.006 (2008-07-16) 1032 */ 1033 protected $linestyleCap = '0 J'; 1034 1035 /** 1036 * PDF string for last line width 1037 * @access protected 1038 * @since 4.0.006 (2008-07-16) 1039 */ 1040 protected $linestyleJoin = '0 j'; 1041 1042 /** 1043 * PDF string for last line width 1044 * @access protected 1045 * @since 4.0.006 (2008-07-16) 1046 */ 1047 protected $linestyleDash = '[] 0 d'; 1048 1049 /** 1050 * True if marked-content sequence is open 1051 * @access protected 1052 * @since 4.0.013 (2008-07-28) 1053 */ 1054 protected $openMarkedContent = false; 1055 1056 /** 1057 * Count the latest inserted vertical spaces on HTML 1058 * @access protected 1059 * @since 4.0.021 (2008-08-24) 1060 */ 1061 protected $htmlvspace = 0; 1062 1063 /** 1064 * Array of Spot colors 1065 * @access protected 1066 * @since 4.0.024 (2008-09-12) 1067 */ 1068 protected $spot_colors = array(); 1069 1070 /** 1071 * Symbol used for HTML unordered list items 1072 * @access protected 1073 * @since 4.0.028 (2008-09-26) 1074 */ 1075 protected $lisymbol = ''; 1076 1077 /** 1078 * String used to mark the beginning and end of EPS image blocks 1079 * @access protected 1080 * @since 4.1.000 (2008-10-18) 1081 */ 1082 protected $epsmarker = 'x#!#EPS#!#x'; 1083 1084 /** 1085 * Array of transformation matrix 1086 * @access protected 1087 * @since 4.2.000 (2008-10-29) 1088 */ 1089 protected $transfmatrix = array(); 1090 1091 /** 1092 * Current key for transformation matrix 1093 * @access protected 1094 * @since 4.8.005 (2009-09-17) 1095 */ 1096 protected $transfmatrix_key = 0; 1097 1098 /** 1099 * Booklet mode for double-sided pages 1100 * @access protected 1101 * @since 4.2.000 (2008-10-29) 1102 */ 1103 protected $booklet = false; 1104 1105 /** 1106 * Epsilon value used for float calculations 1107 * @access protected 1108 * @since 4.2.000 (2008-10-29) 1109 */ 1110 protected $feps = 0.005; 1111 1112 /** 1113 * Array used for custom vertical spaces for HTML tags 1114 * @access protected 1115 * @since 4.2.001 (2008-10-30) 1116 */ 1117 protected $tagvspaces = array(); 1118 1119 /** 1120 * @var HTML PARSER: custom indent amount for lists. 1121 * Negative value means disabled. 1122 * @access protected 1123 * @since 4.2.007 (2008-11-12) 1124 */ 1125 protected $customlistindent = -1; 1126 1127 /** 1128 * @var if true keeps the border open for the cell sides that cross the page. 1129 * @access protected 1130 * @since 4.2.010 (2008-11-14) 1131 */ 1132 protected $opencell = true; 1133 1134 /** 1135 * @var array of files to embedd 1136 * @access protected 1137 * @since 4.4.000 (2008-12-07) 1138 */ 1139 protected $embeddedfiles = array(); 1140 1141 /** 1142 * @var boolean true when inside html pre tag 1143 * @access protected 1144 * @since 4.4.001 (2008-12-08) 1145 */ 1146 protected $premode = false; 1147 1148 /** 1149 * Array used to store positions of graphics transformation blocks inside the page buffer. 1150 * keys are the page numbers 1151 * @access protected 1152 * @since 4.4.002 (2008-12-09) 1153 */ 1154 protected $transfmrk = array(); 1155 1156 /** 1157 * Default color for html links 1158 * @access protected 1159 * @since 4.4.003 (2008-12-09) 1160 */ 1161 protected $htmlLinkColorArray = array(0, 0, 255); 1162 1163 /** 1164 * Default font style to add to html links 1165 * @access protected 1166 * @since 4.4.003 (2008-12-09) 1167 */ 1168 protected $htmlLinkFontStyle = 'U'; 1169 1170 /** 1171 * Counts the number of pages. 1172 * @access protected 1173 * @since 4.5.000 (2008-12-31) 1174 */ 1175 protected $numpages = 0; 1176 1177 /** 1178 * Array containing page lengths in bytes. 1179 * @access protected 1180 * @since 4.5.000 (2008-12-31) 1181 */ 1182 protected $pagelen = array(); 1183 1184 /** 1185 * Counts the number of pages. 1186 * @access protected 1187 * @since 4.5.000 (2008-12-31) 1188 */ 1189 protected $numimages = 0; 1190 1191 /** 1192 * Store the image keys. 1193 * @access protected 1194 * @since 4.5.000 (2008-12-31) 1195 */ 1196 protected $imagekeys = array(); 1197 1198 /** 1199 * Length of the buffer in bytes. 1200 * @access protected 1201 * @since 4.5.000 (2008-12-31) 1202 */ 1203 protected $bufferlen = 0; 1204 1205 /** 1206 * If true enables disk caching. 1207 * @access protected 1208 * @since 4.5.000 (2008-12-31) 1209 */ 1210 protected $diskcache = false; 1211 1212 /** 1213 * Counts the number of fonts. 1214 * @access protected 1215 * @since 4.5.000 (2009-01-02) 1216 */ 1217 protected $numfonts = 0; 1218 1219 /** 1220 * Store the font keys. 1221 * @access protected 1222 * @since 4.5.000 (2009-01-02) 1223 */ 1224 protected $fontkeys = array(); 1225 1226 /** 1227 * Store the font object IDs. 1228 * @access protected 1229 * @since 4.8.001 (2009-09-09) 1230 */ 1231 protected $font_obj_ids = array(); 1232 1233 /** 1234 * Store the fage status (true when opened, false when closed). 1235 * @access protected 1236 * @since 4.5.000 (2009-01-02) 1237 */ 1238 protected $pageopen = array(); 1239 1240 /** 1241 * Default monospaced font 1242 * @access protected 1243 * @since 4.5.025 (2009-03-10) 1244 */ 1245 protected $default_monospaced_font = 'courier'; 1246 1247 /** 1248 * Used to store a cloned copy of the current class object 1249 * @access protected 1250 * @since 4.5.029 (2009-03-19) 1251 */ 1252 protected $objcopy; 1253 1254 /** 1255 * Array used to store the lengths of cache files 1256 * @access protected 1257 * @since 4.5.029 (2009-03-19) 1258 */ 1259 protected $cache_file_length = array(); 1260 1261 /** 1262 * Table header content to be repeated on each new page 1263 * @access protected 1264 * @since 4.5.030 (2009-03-20) 1265 */ 1266 protected $thead = ''; 1267 1268 /** 1269 * Margins used for table header. 1270 * @access protected 1271 * @since 4.5.030 (2009-03-20) 1272 */ 1273 protected $theadMargins = array(); 1274 1275 /** 1276 * Cache array for UTF8StringToArray() method. 1277 * @access protected 1278 * @since 4.5.037 (2009-04-07) 1279 */ 1280 protected $cache_UTF8StringToArray = array(); 1281 1282 /** 1283 * Maximum size of cache array used for UTF8StringToArray() method. 1284 * @access protected 1285 * @since 4.5.037 (2009-04-07) 1286 */ 1287 protected $cache_maxsize_UTF8StringToArray = 8; 1288 1289 /** 1290 * Current size of cache array used for UTF8StringToArray() method. 1291 * @access protected 1292 * @since 4.5.037 (2009-04-07) 1293 */ 1294 protected $cache_size_UTF8StringToArray = 0; 1295 1296 /** 1297 * If true enables document signing 1298 * @access protected 1299 * @since 4.6.005 (2009-04-24) 1300 */ 1301 protected $sign = false; 1302 1303 /** 1304 * Signature data 1305 * @access protected 1306 * @since 4.6.005 (2009-04-24) 1307 */ 1308 protected $signature_data = array(); 1309 1310 /** 1311 * Signature max length 1312 * @access protected 1313 * @since 4.6.005 (2009-04-24) 1314 */ 1315 protected $signature_max_length = 11742; 1316 1317 /** 1318 * Regular expression used to find blank characters used for word-wrapping. 1319 * @access protected 1320 * @since 4.6.006 (2009-04-28) 1321 */ 1322 protected $re_spaces = '/[\s]/'; 1323 1324 /** 1325 * Signature object ID 1326 * @access protected 1327 * @since 4.6.022 (2009-06-23) 1328 */ 1329 protected $sig_obj_id = 0; 1330 1331 /** 1332 * ByteRange placemark used during signature process. 1333 * @access protected 1334 * @since 4.6.028 (2009-08-25) 1335 */ 1336 protected $byterange_string = '/ByteRange[0 ********** ********** **********]'; 1337 1338 /** 1339 * Placemark used during signature process. 1340 * @access protected 1341 * @since 4.6.028 (2009-08-25) 1342 */ 1343 protected $sig_annot_ref = '***SIGANNREF*** 0 R'; 1344 1345 /** 1346 * ID of page objects 1347 * @access protected 1348 * @since 4.7.000 (2009-08-29) 1349 */ 1350 protected $page_obj_id = array(); 1351 1352 /** 1353 * Start ID for embedded file objects 1354 * @access protected 1355 * @since 4.7.000 (2009-08-29) 1356 */ 1357 protected $embedded_start_obj_id = 100000; 1358 1359 /** 1360 * Start ID for annotation objects 1361 * @access protected 1362 * @since 4.7.000 (2009-08-29) 1363 */ 1364 protected $annots_start_obj_id = 200000; 1365 1366 /** 1367 * Max ID of annotation object 1368 * @access protected 1369 * @since 4.7.000 (2009-08-29) 1370 */ 1371 protected $annot_obj_id = 200000; 1372 1373 /** 1374 * Current ID of annotation object 1375 * @access protected 1376 * @since 4.8.003 (2009-09-15) 1377 */ 1378 protected $curr_annot_obj_id = 200000; 1379 1380 /** 1381 * List of form annotations IDs 1382 * @access protected 1383 * @since 4.8.000 (2009-09-07) 1384 */ 1385 protected $form_obj_id = array(); 1386 1387 /** 1388 * Deafult Javascript field properties. Possible values are described on official Javascript for Acrobat API reference. Annotation options can be directly specified using the 'aopt' entry. 1389 * @access protected 1390 * @since 4.8.000 (2009-09-07) 1391 */ 1392 protected $default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128)); 1393 1394 /** 1395 * Javascript objects array 1396 * @access protected 1397 * @since 4.8.000 (2009-09-07) 1398 */ 1399 protected $js_objects = array(); 1400 1401 /** 1402 * Start ID for javascript objects 1403 * @access protected 1404 * @since 4.8.000 (2009-09-07) 1405 */ 1406 protected $js_start_obj_id = 300000; 1407 1408 /** 1409 * Current ID of javascript object 1410 * @access protected 1411 * @since 4.8.000 (2009-09-07) 1412 */ 1413 protected $js_obj_id = 300000; 1414 1415 /** 1416 * Current form action (used during XHTML rendering) 1417 * @access protected 1418 * @since 4.8.000 (2009-09-07) 1419 */ 1420 protected $form_action = ''; 1421 1422 /** 1423 * Current form encryption type (used during XHTML rendering) 1424 * @access protected 1425 * @since 4.8.000 (2009-09-07) 1426 */ 1427 protected $form_enctype = 'application/x-www-form-urlencoded'; 1428 1429 /** 1430 * Current method to submit forms. 1431 * @access protected 1432 * @since 4.8.000 (2009-09-07) 1433 */ 1434 protected $form_mode = 'post'; 1435 1436 /** 1437 * Start ID for appearance streams XObjects 1438 * @access protected 1439 * @since 4.8.001 (2009-09-09) 1440 */ 1441 protected $apxo_start_obj_id = 400000; 1442 1443 /** 1444 * Current ID of appearance streams XObjects 1445 * @access protected 1446 * @since 4.8.001 (2009-09-09) 1447 */ 1448 protected $apxo_obj_id = 400000; 1449 1450 /** 1451 * List of fonts used on form fields (fontname => fontkey). 1452 * @access protected 1453 * @since 4.8.001 (2009-09-09) 1454 */ 1455 protected $annotation_fonts = array(); 1456 1457 /** 1458 * List of radio buttons parent objects. 1459 * @access protected 1460 * @since 4.8.001 (2009-09-09) 1461 */ 1462 protected $radiobutton_groups = array(); 1463 1464 /** 1465 * List of radio group objects IDs 1466 * @access protected 1467 * @since 4.8.001 (2009-09-09) 1468 */ 1469 protected $radio_groups = array(); 1470 1471 /** 1472 * Text indentation value (used for text-indent CSS attribute) 1473 * @access protected 1474 * @since 4.8.006 (2009-09-23) 1475 */ 1476 protected $textindent = 0; 1477 1478 /** 1479 * Store page number when startTransaction() is called. 1480 * @access protected 1481 * @since 4.8.006 (2009-09-23) 1482 */ 1483 protected $start_transaction_page = 0; 1484 1485 /** 1486 * Store Y position when startTransaction() is called. 1487 * @access protected 1488 * @since 4.9.001 (2010-03-28) 1489 */ 1490 protected $start_transaction_y = 0; 1491 1492 /** 1493 * True when we are printing the thead section on a new page 1494 * @access protected 1495 * @since 4.8.027 (2010-01-25) 1496 */ 1497 protected $inthead = false; 1498 1499 /** 1500 * Array of column measures (width, space, starting Y position) 1501 * @access protected 1502 * @since 4.9.001 (2010-03-28) 1503 */ 1504 protected $columns = array(); 1505 1506 /** 1507 * Number of colums 1508 * @access protected 1509 * @since 4.9.001 (2010-03-28) 1510 */ 1511 protected $num_columns = 0; 1512 1513 /** 1514 * Current column number 1515 * @access protected 1516 * @since 4.9.001 (2010-03-28) 1517 */ 1518 protected $current_column = 0; 1519 1520 /** 1521 * Starting page for columns 1522 * @access protected 1523 * @since 4.9.001 (2010-03-28) 1524 */ 1525 protected $column_start_page = 0; 1526 1527 /** 1528 * Text rendering mode: 0 = Fill text; 1 = Stroke text; 2 = Fill, then stroke text; 3 = Neither fill nor stroke text (invisible); 4 = Fill text and add to path for clipping; 5 = Stroke text and add to path for clipping; 6 = Fill, then stroke text and add to path for clipping; 7 = Add text to path for clipping. 1529 * @access protected 1530 * @since 4.9.008 (2010-04-03) 1531 */ 1532 protected $textrendermode = 0; 1533 1534 /** 1535 * Text stroke width in doc units 1536 * @access protected 1537 * @since 4.9.008 (2010-04-03) 1538 */ 1539 protected $textstrokewidth = 0; 1540 1541 /** 1542 * @var current stroke color 1543 * @access protected 1544 * @since 4.9.008 (2010-04-03) 1545 */ 1546 protected $strokecolor; 1547 1548 /** 1549 * @var default unit of measure for document 1550 * @access protected 1551 * @since 5.0.000 (2010-04-22) 1552 */ 1553 protected $pdfunit = 'mm'; 1554 1555 /** 1556 * @var true when we are on TOC (Table Of Content) page 1557 * @access protected 1558 */ 1559 protected $tocpage = false; 1560 1561 /** 1562 * @var If true convert vector images (SVG, EPS) to raster image using GD or ImageMagick library. 1563 * @access protected 1564 * @since 5.0.000 (2010-04-26) 1565 */ 1566 protected $rasterize_vector_images = false; 1567 1568 /** 1569 * @var directory used for the last SVG image 1570 * @access protected 1571 * @since 5.0.000 (2010-05-05) 1572 */ 1573 protected $svgdir = ''; 1574 1575 /** 1576 * @var Deafult unit of measure for SVG 1577 * @access protected 1578 * @since 5.0.000 (2010-05-02) 1579 */ 1580 protected $svgunit = 'px'; 1581 1582 /** 1583 * @var array of SVG gradients 1584 * @access protected 1585 * @since 5.0.000 (2010-05-02) 1586 */ 1587 protected $svggradients = array(); 1588 1589 /** 1590 * @var ID of last SVG gradient 1591 * @access protected 1592 * @since 5.0.000 (2010-05-02) 1593 */ 1594 protected $svggradientid = 0; 1595 1596 /** 1597 * @var true when in SVG defs group 1598 * @access protected 1599 * @since 5.0.000 (2010-05-02) 1600 */ 1601 protected $svgdefsmode = false; 1602 1603 /** 1604 * @var array of SVG defs 1605 * @access protected 1606 * @since 5.0.000 (2010-05-02) 1607 */ 1608 protected $svgdefs = array(); 1609 1610 /** 1611 * @var true when in SVG clipPath tag 1612 * @access protected 1613 * @since 5.0.000 (2010-04-26) 1614 */ 1615 protected $svgclipmode = false; 1616 1617 /** 1618 * @var array of SVG clipPath commands 1619 * @access protected 1620 * @since 5.0.000 (2010-05-02) 1621 */ 1622 protected $svgclippaths = array(); 1623 1624 /** 1625 * @var ID of last SVG clipPath 1626 * @access protected 1627 * @since 5.0.000 (2010-05-02) 1628 */ 1629 protected $svgclipid = 0; 1630 1631 /** 1632 * @var svg text 1633 * @access protected 1634 * @since 5.0.000 (2010-05-02) 1635 */ 1636 protected $svgtext = ''; 1637 1638 /** 1639 * @var array of hinheritable SVG properties 1640 * @access protected 1641 * @since 5.0.000 (2010-05-02) 1642 */ 1643 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'); 1644 1645 /** 1646 * @var array of SVG properties 1647 * @access protected 1648 * @since 5.0.000 (2010-05-02) 1649 */ 1650 protected $svgstyles = array(array( 1651 'alignment-baseline' => 'auto', 1652 'baseline-shift' => 'baseline', 1653 'clip' => 'auto', 1654 'clip-path' => 'none', 1655 'clip-rule' => 'nonzero', 1656 'color' => 'black', 1657 'color-interpolation' => 'sRGB', 1658 'color-interpolation-filters' => 'linearRGB', 1659 'color-profile' => 'auto', 1660 'color-rendering' => 'auto', 1661 'cursor' => 'auto', 1662 'direction' => 'ltr', 1663 'display' => 'inline', 1664 'dominant-baseline' => 'auto', 1665 'enable-background' => 'accumulate', 1666 'fill' => 'black', 1667 'fill-opacity' => 1, 1668 'fill-rule' => 'nonzero', 1669 'filter' => 'none', 1670 'flood-color' => 'black', 1671 'flood-opacity' => 1, 1672 'font' => '', 1673 'font-family' => 'helvetica', 1674 'font-size' => 'medium', 1675 'font-size-adjust' => 'none', 1676 'font-stretch' => 'normal', 1677 'font-style' => 'normal', 1678 'font-variant' => 'normal', 1679 'font-weight' => 'normal', 1680 'glyph-orientation-horizontal' => '0deg', 1681 'glyph-orientation-vertical' => 'auto', 1682 'image-rendering' => 'auto', 1683 'kerning' => 'auto', 1684 'letter-spacing' => 'normal', 1685 'lighting-color' => 'white', 1686 'marker' => '', 1687 'marker-end' => 'none', 1688 'marker-mid' => 'none', 1689 'marker-start' => 'none', 1690 'mask' => 'none', 1691 'opacity' => 1, 1692 'overflow' => 'auto', 1693 'pointer-events' => 'visiblePainted', 1694 'shape-rendering' => 'auto', 1695 'stop-color' => 'black', 1696 'stop-opacity' => 1, 1697 'stroke' => 'none', 1698 'stroke-dasharray' => 'none', 1699 'stroke-dashoffset' => 0, 1700 'stroke-linecap' => 'butt', 1701 'stroke-linejoin' => 'miter', 1702 'stroke-miterlimit' => 4, 1703 'stroke-opacity' => 1, 1704 'stroke-width' => 1, 1705 'text-anchor' => 'start', 1706 'text-decoration' => 'none', 1707 'text-rendering' => 'auto', 1708 'unicode-bidi' => 'normal', 1709 'visibility' => 'visible', 1710 'word-spacing' => 'normal', 1711 'writing-mode' => 'lr-tb', 1712 'text-color' => 'black', 1713 'transfmatrix' => array(1, 0, 0, 1, 0, 0) 1714 )); 1715 1716 //------------------------------------------------------------ 1717 // METHODS 1718 //------------------------------------------------------------ 1719 1720 /** 1721 * This is the class constructor. 1722 * It allows to set up the page format, the orientation and 1723 * the measure unit used in all the methods (except for the font sizes). 1724 * @since 1.0 1725 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li></ul> 1726 * @param string $unit User measure unit. Possible values are:<ul><li>pt: point</li><li>mm: millimeter (default)</li><li>cm: centimeter</li><li>in: inch</li></ul><br />A point equals 1/72 of inch, that is to say about 0.35 mm (an inch being 2.54 cm). This is a very common unit in typography; font sizes are expressed in that unit. 1727 * @param mixed $format The format used for pages. It can be either one of the following values (case insensitive) or a custom format in the form of a two-element array containing the width and the height (expressed in the unit given by unit).<ul><li>4A0</li><li>2A0</li><li>A0</li><li>A1</li><li>A2</li><li>A3</li><li>A4 (default)</li><li>A5</li><li>A6</li><li>A7</li><li>A8</li><li>A9</li><li>A10</li><li>B0</li><li>B1</li><li>B2</li><li>B3</li><li>B4</li><li>B5</li><li>B6</li><li>B7</li><li>B8</li><li>B9</li><li>B10</li><li>C0</li><li>C1</li><li>C2</li><li>C3</li><li>C4</li><li>C5</li><li>C6</li><li>C7</li><li>C8</li><li>C9</li><li>C10</li><li>RA0</li><li>RA1</li><li>RA2</li><li>RA3</li><li>RA4</li><li>SRA0</li><li>SRA1</li><li>SRA2</li><li>SRA3</li><li>SRA4</li><li>LETTER</li><li>LEGAL</li><li>EXECUTIVE</li><li>FOLIO</li></ul> 1728 * @param boolean $unicode TRUE means that the input text is unicode (default = true) 1729 * @param boolean $diskcache if TRUE reduce the RAM memory usage by caching temporary data on filesystem (slower). 1730 * @param String $encoding charset encoding; default is UTF-8 1731 * @access public 1732 */ 1733 public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false) { 1734 /* Set internal character encoding to ASCII */ 1735 if (function_exists('mb_internal_encoding') AND mb_internal_encoding()) { 1736 $this->internal_encoding = mb_internal_encoding(); 1737 mb_internal_encoding('ASCII'); 1738 } 1739 // set disk caching 1740 $this->diskcache = $diskcache ? true : false; 1741 // set language direction 1742 $this->rtl = false; 1743 $this->tmprtl = false; 1744 //Some checks 1745 $this->_dochecks(); 1746 //Initialization of properties 1747 $this->isunicode = $unicode; 1748 $this->page = 0; 1749 $this->transfmrk[0] = array(); 1750 $this->pagedim = array(); 1751 $this->n = 2; 1752 $this->buffer = ''; 1753 $this->pages = array(); 1754 $this->state = 0; 1755 $this->fonts = array(); 1756 $this->FontFiles = array(); 1757 $this->diffs = array(); 1758 $this->images = array(); 1759 $this->links = array(); 1760 $this->gradients = array(); 1761 $this->InFooter = false; 1762 $this->lasth = 0; 1763 $this->FontFamily = 'helvetica'; 1764 $this->FontStyle = ''; 1765 $this->FontSizePt = 12; 1766 $this->underline = false; 1767 $this->overline = false; 1768 $this->linethrough = false; 1769 $this->DrawColor = '0 G'; 1770 $this->FillColor = '0 g'; 1771 $this->TextColor = '0 g'; 1772 $this->ColorFlag = false; 1773 // encryption values 1774 $this->encrypted = false; 1775 $this->last_rc4_key = ''; 1776 $this->padding = "\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A"; 1777 //Standard Unicode fonts 1778 $this->CoreFonts = array( 1779 'courier'=>'Courier', 1780 'courierB'=>'Courier-Bold', 1781 'courierI'=>'Courier-Oblique', 1782 'courierBI'=>'Courier-BoldOblique', 1783 'helvetica'=>'Helvetica', 1784 'helveticaB'=>'Helvetica-Bold', 1785 'helveticaI'=>'Helvetica-Oblique', 1786 'helveticaBI'=>'Helvetica-BoldOblique', 1787 'times'=>'Times-Roman', 1788 'timesB'=>'Times-Bold', 1789 'timesI'=>'Times-Italic', 1790 'timesBI'=>'Times-BoldItalic', 1791 'symbol'=>'Symbol', 1792 'zapfdingbats'=>'ZapfDingbats' 1793 ); 1794 //Set scale factor 1795 $this->setPageUnit($unit); 1796 // set page format and orientation 1797 $this->setPageFormat($format, $orientation); 1798 //Page margins (1 cm) 1799 $margin = 28.35 / $this->k; 1800 $this->SetMargins($margin, $margin); 1801 //Interior cell margin 1802 $this->cMargin = $margin / 10; 1803 //Line width (0.2 mm) 1804 $this->LineWidth = 0.57 / $this->k; 1805 $this->linestyleWidth = sprintf('%.2F w', ($this->LineWidth * $this->k)); 1806 $this->linestyleCap = '0 J'; 1807 $this->linestyleJoin = '0 j'; 1808 $this->linestyleDash = '[] 0 d'; 1809 //Automatic page break 1810 $this->SetAutoPageBreak(true, (2 * $margin)); 1811 //Full width display mode 1812 $this->SetDisplayMode('fullwidth'); 1813 //Compression 1814 $this->SetCompression(true); 1815 //Set default PDF version number 1816 $this->PDFVersion = '1.7'; 1817 $this->encoding = $encoding; 1818 $this->HREF = array(); 1819 $this->getFontsList(); 1820 $this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0); 1821 $this->strokecolor = array('R' => 0, 'G' => 0, 'B' => 0); 1822 $this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255); 1823 $this->extgstates = array(); 1824 // user's rights 1825 $this->sign = false; 1826 $this->ur = false; 1827 $this->ur_document = '/FullSave'; 1828 $this->ur_annots = '/Create/Delete/Modify/Copy/Import/Export'; 1829 $this->ur_form = '/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate'; 1830 $this->ur_signature = '/Modify'; 1831 // set default JPEG quality 1832 $this->jpeg_quality = 75; 1833 // initialize some settings 1834 $this->utf8Bidi(array(''), ''); 1835 // set default font 1836 $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt); 1837 // check if PCRE Unicode support is enabled 1838 if ($this->isunicode AND (@preg_match('/\pL/u', 'a') == 1)) { 1839 // PCRE unicode support is turned ON 1840 // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator. 1841 // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants. 1842 // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between. 1843 //$this->re_spaces = '/[\s\p{Z}\p{Lo}]/u'; 1844 $this->re_spaces = '/[\s\p{Z}]/u'; 1845 } else { 1846 // PCRE unicode support is turned OFF 1847 $this->re_spaces = '/[\s]/'; 1848 } 1849 $this->annot_obj_id = $this->annots_start_obj_id; 1850 $this->curr_annot_obj_id = $this->annots_start_obj_id; 1851 $this->apxo_obj_id = $this->apxo_start_obj_id; 1852 $this->js_obj_id = $this->js_start_obj_id; 1853 $this->default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128)); 1854 } 1855 1856 /** 1857 * Default destructor. 1858 * @access public 1859 * @since 1.53.0.TC016 1860 */ 1861 public function __destruct() { 1862 // restore internal encoding 1863 if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) { 1864 mb_internal_encoding($this->internal_encoding); 1865 } 1866 // unset all class variables 1867 $this->_destroy(true); 1868 } 1869 1870 /** 1871 * Set the units of measure for the document. 1872 * @param string $unit User measure unit. Possible values are:<ul><li>pt: point</li><li>mm: millimeter (default)</li><li>cm: centimeter</li><li>in: inch</li></ul><br />A point equals 1/72 of inch, that is to say about 0.35 mm (an inch being 2.54 cm). This is a very common unit in typography; font sizes are expressed in that unit. 1873 * @access public 1874 * @since 3.0.015 (2008-06-06) 1875 */ 1876 public function setPageUnit($unit) { 1877 $unit = strtolower($unit); 1878 //Set scale factor 1879 switch ($unit) { 1880 // points 1881 case 'px': 1882 case 'pt': { 1883 $this->k = 1; 1884 break; 1885 } 1886 // millimeters 1887 case 'mm': { 1888 $this->k = $this->dpi / 25.4; 1889 break; 1890 } 1891 // centimeters 1892 case 'cm': { 1893 $this->k = $this->dpi / 2.54; 1894 break; 1895 } 1896 // inches 1897 case 'in': { 1898 $this->k = $this->dpi; 1899 break; 1900 } 1901 // unsupported unit 1902 default : { 1903 $this->Error('Incorrect unit: '.$unit); 1904 break; 1905 } 1906 } 1907 $this->pdfunit = $unit; 1908 if (isset($this->CurOrientation)) { 1909 $this->setPageOrientation($this->CurOrientation); 1910 } 1911 } 1912 1913 /** 1914 * Set the page format 1915 * @param mixed $format The format used for pages. It can be either one of the following values (case insensitive) or a custom format in the form of a two-element array containing the width and the height (expressed in the unit given by unit).<ul><li>4A0</li><li>2A0</li><li>A0</li><li>A1</li><li>A2</li><li>A3</li><li>A4 (default)</li><li>A5</li><li>A6</li><li>A7</li><li>A8</li><li>A9</li><li>A10</li><li>B0</li><li>B1</li><li>B2</li><li>B3</li><li>B4</li><li>B5</li><li>B6</li><li>B7</li><li>B8</li><li>B9</li><li>B10</li><li>C0</li><li>C1</li><li>C2</li><li>C3</li><li>C4</li><li>C5</li><li>C6</li><li>C7</li><li>C8</li><li>C9</li><li>C10</li><li>RA0</li><li>RA1</li><li>RA2</li><li>RA3</li><li>RA4</li><li>SRA0</li><li>SRA1</li><li>SRA2</li><li>SRA3</li><li>SRA4</li><li>LETTER</li><li>LEGAL</li><li>EXECUTIVE</li><li>FOLIO</li></ul> 1916 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul> 1917 * @access public 1918 * @since 3.0.015 (2008-06-06) 1919 */ 1920 public function setPageFormat($format, $orientation='P') { 1921 //Page format 1922 if (is_string($format)) { 1923 // Page formats (45 standard ISO paper formats and 4 american common formats). 1924 // Paper cordinates are calculated in this way: (inches * 72) where (1 inch = 2.54 cm) 1925 switch (strtoupper($format)) { 1926 case '4A0': {$format = array(4767.87,6740.79); break;} 1927 case '2A0': {$format = array(3370.39,4767.87); break;} 1928 case 'A0': {$format = array(2383.94,3370.39); break;} 1929 case 'A1': {$format = array(1683.78,2383.94); break;} 1930 case 'A2': {$format = array(1190.55,1683.78); break;} 1931 case 'A3': {$format = array(841.89,1190.55); break;} 1932 case 'A4': default: {$format = array(595.28,841.89); break;} 1933 case 'A5': {$format = array(419.53,595.28); break;} 1934 case 'A6': {$format = array(297.64,419.53); break;} 1935 case 'A7': {$format = array(209.76,297.64); break;} 1936 case 'A8': {$format = array(147.40,209.76); break;} 1937 case 'A9': {$format = array(104.88,147.40); break;} 1938 case 'A10': {$format = array(73.70,104.88); break;} 1939 case 'B0': {$format = array(2834.65,4008.19); break;} 1940 case 'B1': {$format = array(2004.09,2834.65); break;} 1941 case 'B2': {$format = array(1417.32,2004.09); break;} 1942 case 'B3': {$format = array(1000.63,1417.32); break;} 1943 case 'B4': {$format = array(708.66,1000.63); break;} 1944 case 'B5': {$format = array(498.90,708.66); break;} 1945 case 'B6': {$format = array(354.33,498.90); break;} 1946 case 'B7': {$format = array(249.45,354.33); break;} 1947 case 'B8': {$format = array(175.75,249.45); break;} 1948 case 'B9': {$format = array(124.72,175.75); break;} 1949 case 'B10': {$format = array(87.87,124.72); break;} 1950 case 'C0': {$format = array(2599.37,3676.54); break;} 1951 case 'C1': {$format = array(1836.85,2599.37); break;} 1952 case 'C2': {$format = array(1298.27,1836.85); break;} 1953 case 'C3': {$format = array(918.43,1298.27); break;} 1954 case 'C4': {$format = array(649.13,918.43); break;} 1955 case 'C5': {$format = array(459.21,649.13); break;} 1956 case 'C6': {$format = array(323.15,459.21); break;} 1957 case 'C7': {$format = array(229.61,323.15); break;} 1958 case 'C8': {$format = array(161.57,229.61); break;} 1959 case 'C9': {$format = array(113.39,161.57); break;} 1960 case 'C10': {$format = array(79.37,113.39); break;} 1961 case 'RA0': {$format = array(2437.80,3458.27); break;} 1962 case 'RA1': {$format = array(1729.13,2437.80); break;} 1963 case 'RA2': {$format = array(1218.90,1729.13); break;} 1964 case 'RA3': {$format = array(864.57,1218.90); break;} 1965 case 'RA4': {$format = array(609.45,864.57); break;} 1966 case 'SRA0': {$format = array(2551.18,3628.35); break;} 1967 case 'SRA1': {$format = array(1814.17,2551.18); break;} 1968 case 'SRA2': {$format = array(1275.59,1814.17); break;} 1969 case 'SRA3': {$format = array(907.09,1275.59); break;} 1970 case 'SRA4': {$format = array(637.80,907.09); break;} 1971 case 'LETTER': {$format = array(612.00,792.00); break;} 1972 case 'LEGAL': {$format = array(612.00,1008.00); break;} 1973 case 'EXECUTIVE': {$format = array(521.86,756.00); break;} 1974 case 'FOLIO': {$format = array(612.00,936.00); break;} 1975 } 1976 $this->fwPt = $format[0]; 1977 $this->fhPt = $format[1]; 1978 } else { 1979 $this->fwPt = $format[0] * $this->k; 1980 $this->fhPt = $format[1] * $this->k; 1981 } 1982 $this->setPageOrientation($orientation); 1983 } 1984 1985 /** 1986 * Set page orientation. 1987 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul> 1988 * @param boolean $autopagebreak Boolean indicating if auto-page-break mode should be on or off. 1989 * @param float $bottommargin bottom margin of the page. 1990 * @access public 1991 * @since 3.0.015 (2008-06-06) 1992 */ 1993 public function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') { 1994 if ($this->fwPt > $this->fhPt) { 1995 // landscape 1996 $default_orientation = 'L'; 1997 } else { 1998 // portrait 1999 $default_orientation = 'P'; 2000 } 2001 $valid_orientations = array('P', 'L'); 2002 if (empty($orientation)) { 2003 $orientation = $default_orientation; 2004 } else { 2005 $orientation = $orientation{0}; 2006 $orientation = strtoupper($orientation); 2007 } 2008 if (in_array($orientation, $valid_orientations) AND ($orientation != $default_orientation)) { 2009 $this->CurOrientation = $orientation; 2010 $this->wPt = $this->fhPt; 2011 $this->hPt = $this->fwPt; 2012 } else { 2013 $this->CurOrientation = $default_orientation; 2014 $this->wPt = $this->fwPt; 2015 $this->hPt = $this->fhPt; 2016 } 2017 $this->w = $this->wPt / $this->k; 2018 $this->h = $this->hPt / $this->k; 2019 if ($this->empty_string($autopagebreak)) { 2020 if (isset($this->AutoPageBreak)) { 2021 $autopagebreak = $this->AutoPageBreak; 2022 } else { 2023 $autopagebreak = true; 2024 } 2025 } 2026 if ($this->empty_string($bottommargin)) { 2027 if (isset($this->bMargin)) { 2028 $bottommargin = $this->bMargin; 2029 } else { 2030 // default value = 2 cm 2031 $bottommargin = 2 * 28.35 / $this->k; 2032 } 2033 } 2034 $this->SetAutoPageBreak($autopagebreak, $bottommargin); 2035 // store page dimensions 2036 $this->pagedim[$this->page] = array('w' => $this->wPt, 'h' => $this->hPt, 'wk' => $this->w, 'hk' => $this->h, 'tm' => $this->tMargin, 'bm' => $bottommargin, 'lm' => $this->lMargin, 'rm' => $this->rMargin, 'pb' => $autopagebreak, 'or' => $this->CurOrientation, 'olm' => $this->original_lMargin, 'orm' => $this->original_rMargin); 2037 } 2038 2039 /** 2040 * Set regular expression to detect withespaces or word separators. 2041 * @param string $re regular expression (leave empty for default). 2042 * @access public 2043 * @since 4.6.016 (2009-06-15) 2044 */ 2045 public function setSpacesRE($re='/[\s]/') { 2046 // if PCRE unicode support is turned ON: 2047 // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator. 2048 // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants. 2049 // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between. 2050 $this->re_spaces = $re; 2051 } 2052 2053 /** 2054 * Enable or disable Right-To-Left language mode 2055 * @param Boolean $enable if true enable Right-To-Left language mode. 2056 * @param Boolean $resetx if true reset the X position on direction change. 2057 * @access public 2058 * @since 2.0.000 (2008-01-03) 2059 */ 2060 public function setRTL($enable, $resetx=true) { 2061 $enable = $enable ? true : false; 2062 $resetx = ($resetx AND ($enable != $this->rtl)); 2063 $this->rtl = $enable; 2064 $this->tmprtl = false; 2065 if ($resetx) { 2066 $this->Ln(0); 2067 } 2068 } 2069 2070 /** 2071 * Return the RTL status 2072 * @return boolean 2073 * @access public 2074 * @since 4.0.012 (2008-07-24) 2075 */ 2076 public function getRTL() { 2077 return $this->rtl; 2078 } 2079 2080 /** 2081 * Force temporary RTL language direction 2082 * @param mixed $mode can be false, 'L' for LTR or 'R' for RTL 2083 * @access public 2084 * @since 2.1.000 (2008-01-09) 2085 */ 2086 public function setTempRTL($mode) { 2087 $newmode = false; 2088 switch ($mode) { 2089 case 'ltr': 2090 case 'LTR': 2091 case 'L': { 2092 if ($this->rtl) { 2093 $newmode = 'L'; 2094 } 2095 break; 2096 } 2097 case 'rtl': 2098 case 'RTL': 2099 case 'R': { 2100 if (!$this->rtl) { 2101 $newmode = 'R'; 2102 } 2103 break; 2104 } 2105 case false: 2106 default: { 2107 $newmode = false; 2108 break; 2109 } 2110 } 2111 $this->tmprtl = $newmode; 2112 } 2113 2114 /** 2115 * Return the current temporary RTL status 2116 * @return boolean 2117 * @access public 2118 * @since 4.8.014 (2009-11-04) 2119 */ 2120 public function isRTLTextDir() { 2121 return ($this->rtl OR ($this->tmprtl == 'R')); 2122 } 2123 2124 /** 2125 * Set the last cell height. 2126 * @param float $h cell height. 2127 * @author Nicola Asuni 2128 * @access public 2129 * @since 1.53.0.TC034 2130 */ 2131 public function setLastH($h) { 2132 $this->lasth = $h; 2133 } 2134 2135 /** 2136 * Get the last cell height. 2137 * @return last cell height 2138 * @access public 2139 * @since 4.0.017 (2008-08-05) 2140 */ 2141 public function getLastH() { 2142 return $this->lasth; 2143 } 2144 2145 /** 2146 * Set the adjusting factor to convert pixels to user units. 2147 * @param float $scale adjusting factor to convert pixels to user units. 2148 * @author Nicola Asuni 2149 * @access public 2150 * @since 1.5.2 2151 */ 2152 public function setImageScale($scale) { 2153 $this->imgscale = $scale; 2154 } 2155 2156 /** 2157 * Returns the adjusting factor to convert pixels to user units. 2158 * @return float adjusting factor to convert pixels to user units. 2159 * @author Nicola Asuni 2160 * @access public 2161 * @since 1.5.2 2162 */ 2163 public function getImageScale() { 2164 return $this->imgscale; 2165 } 2166 2167 /** 2168 * Returns an array of page dimensions: 2169 * <ul><li>$this->pagedim[$this->page]['w'] => page_width_in_points</li><li>$this->pagedim[$this->page]['h'] => height in points</li><li>$this->pagedim[$this->page]['wk'] => page_width_in_points</li><li>$this->pagedim[$this->page]['hk'] => height</li><li>$this->pagedim[$this->page]['tm'] => top_margin</li><li>$this->pagedim[$this->page]['bm'] => bottom_margin</li><li>$this->pagedim[$this->page]['lm'] => left_margin</li><li>$this->pagedim[$this->page]['rm'] => right_margin</li><li>$this->pagedim[$this->page]['pb'] => auto_page_break</li><li>$this->pagedim[$this->page]['or'] => page_orientation</li><li>$this->pagedim[$this->page]['olm'] => original_left_margin</li><li>$this->pagedim[$this->page]['orm'] => original_right_margin</li></ul> 2170 * @param int $pagenum page number (empty = current page) 2171 * @return array of page dimensions. 2172 * @author Nicola Asuni 2173 * @access public 2174 * @since 4.5.027 (2009-03-16) 2175 */ 2176 public function getPageDimensions($pagenum='') { 2177 if (empty($pagenum)) { 2178 $pagenum = $this->page; 2179 } 2180 return $this->pagedim[$pagenum]; 2181 } 2182 2183 /** 2184 * Returns the page width in units. 2185 * @param int $pagenum page number (empty = current page) 2186 * @return int page width. 2187 * @author Nicola Asuni 2188 * @access public 2189 * @since 1.5.2 2190 * @see getPageDimensions() 2191 */ 2192 public function getPageWidth($pagenum='') { 2193 if (empty($pagenum)) { 2194 return $this->w; 2195 } 2196 return $this->pagedim[$pagenum]['w']; 2197 } 2198 2199 /** 2200 * Returns the page height in units. 2201 * @param int $pagenum page number (empty = current page) 2202 * @return int page height. 2203 * @author Nicola Asuni 2204 * @access public 2205 * @since 1.5.2 2206 * @see getPageDimensions() 2207 */ 2208 public function getPageHeight($pagenum='') { 2209 if (empty($pagenum)) { 2210 return $this->h; 2211 } 2212 return $this->pagedim[$pagenum]['h']; 2213 } 2214 2215 /** 2216 * Returns the page break margin. 2217 * @param int $pagenum page number (empty = current page) 2218 * @return int page break margin. 2219 * @author Nicola Asuni 2220 * @access public 2221 * @since 1.5.2 2222 * @see getPageDimensions() 2223 */ 2224 public function getBreakMargin($pagenum='') { 2225 if (empty($pagenum)) { 2226 return $this->bMargin; 2227 } 2228 return $this->pagedim[$pagenum]['bm']; 2229 } 2230 2231 /** 2232 * Returns the scale factor (number of points in user unit). 2233 * @return int scale factor. 2234 * @author Nicola Asuni 2235 * @access public 2236 * @since 1.5.2 2237 */ 2238 public function getScaleFactor() { 2239 return $this->k; 2240 } 2241 2242 /** 2243 * Defines the left, top and right margins. 2244 * @param float $left Left margin. 2245 * @param float $top Top margin. 2246 * @param float $right Right margin. Default value is the left one. 2247 * @param boolean $keepmargins if true overwrites the default page margins 2248 * @access public 2249 * @since 1.0 2250 * @see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak() 2251 */ 2252 public function SetMargins($left, $top, $right=-1, $keepmargins=false) { 2253 //Set left, top and right margins 2254 $this->lMargin = $left; 2255 $this->tMargin = $top; 2256 if ($right == -1) { 2257 $right = $left; 2258 } 2259 $this->rMargin = $right; 2260 if ($keepmargins) { 2261 // overwrite original values 2262 $this->original_lMargin = $this->lMargin; 2263 $this->original_rMargin = $this->rMargin; 2264 } 2265 } 2266 2267 /** 2268 * Defines the left margin. The method can be called before creating the first page. If the current abscissa gets out of page, it is brought back to the margin. 2269 * @param float $margin The margin. 2270 * @access public 2271 * @since 1.4 2272 * @see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins() 2273 */ 2274 public function SetLeftMargin($margin) { 2275 //Set left margin 2276 $this->lMargin=$margin; 2277 if (($this->page > 0) AND ($this->x < $margin)) { 2278 $this->x = $margin; 2279 } 2280 } 2281 2282 /** 2283 * Defines the top margin. The method can be called before creating the first page. 2284 * @param float $margin The margin. 2285 * @access public 2286 * @since 1.5 2287 * @see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins() 2288 */ 2289 public function SetTopMargin($margin) { 2290 //Set top margin 2291 $this->tMargin=$margin; 2292 if (($this->page > 0) AND ($this->y < $margin)) { 2293 $this->y = $margin; 2294 } 2295 } 2296 2297 /** 2298 * Defines the right margin. The method can be called before creating the first page. 2299 * @param float $margin The margin. 2300 * @access public 2301 * @since 1.5 2302 * @see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins() 2303 */ 2304 public function SetRightMargin($margin) { 2305 $this->rMargin=$margin; 2306 if (($this->page > 0) AND ($this->x > ($this->w - $margin))) { 2307 $this->x = $this->w - $margin; 2308 } 2309 } 2310 2311 /** 2312 * Set the internal Cell padding. 2313 * @param float $pad internal padding. 2314 * @access public 2315 * @since 2.1.000 (2008-01-09) 2316 * @see Cell(), SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins() 2317 */ 2318 public function SetCellPadding($pad) { 2319 $this->cMargin = $pad; 2320 } 2321 2322 /** 2323 * Enables or disables the automatic page breaking mode. When enabling, the second parameter is the distance from the bottom of the page that defines the triggering limit. By default, the mode is on and the margin is 2 cm. 2324 * @param boolean $auto Boolean indicating if mode should be on or off. 2325 * @param float $margin Distance from the bottom of the page. 2326 * @access public 2327 * @since 1.0 2328 * @see Cell(), MultiCell(), AcceptPageBreak() 2329 */ 2330 public function SetAutoPageBreak($auto, $margin=0) { 2331 //Set auto page break mode and triggering margin 2332 $this->AutoPageBreak = $auto; 2333 $this->bMargin = $margin; 2334 $this->PageBreakTrigger = $this->h - $margin; 2335 } 2336 2337 /** 2338 * Defines the way the document is to be displayed by the viewer. 2339 * @param mixed $zoom The zoom to use. It can be one of the following string values or a number indicating the zooming factor to use. <ul><li>fullpage: displays the entire page on screen </li><li>fullwidth: uses maximum width of window</li><li>real: uses real size (equivalent to 100% zoom)</li><li>default: uses viewer default mode</li></ul> 2340 * @param string $layout The page layout. Possible values are:<ul><li>SinglePage Display one page at a time</li><li>OneColumn Display the pages in one column</li><li>TwoColumnLeft Display the pages in two columns, with odd-numbered pages on the left</li><li>TwoColumnRight Display the pages in two columns, with odd-numbered pages on the right</li><li>TwoPageLeft (PDF 1.5) Display the pages two at a time, with odd-numbered pages on the left</li><li>TwoPageRight (PDF 1.5) Display the pages two at a time, with odd-numbered pages on the right</li></ul> 2341 * @param string $mode A name object specifying how the document should be displayed when opened:<ul><li>UseNone Neither document outline nor thumbnail images visible</li><li>UseOutlines Document outline visible</li><li>UseThumbs Thumbnail images visible</li><li>FullScreen Full-screen mode, with no menu bar, window controls, or any other window visible</li><li>UseOC (PDF 1.5) Optional content group panel visible</li><li>UseAttachments (PDF 1.6) Attachments panel visible</li></ul> 2342 * @access public 2343 * @since 1.2 2344 */ 2345 public function SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone') { 2346 //Set display mode in viewer 2347 if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) { 2348 $this->ZoomMode = $zoom; 2349 } else { 2350 $this->Error('Incorrect zoom display mode: '.$zoom); 2351 } 2352 switch ($layout) { 2353 case 'default': 2354 case 'single': 2355 case 'SinglePage': { 2356 $this->LayoutMode = 'SinglePage'; 2357 break; 2358 } 2359 case 'continuous': 2360 case 'OneColumn': { 2361 $this->LayoutMode = 'OneColumn'; 2362 break; 2363 } 2364 case 'two': 2365 case 'TwoColumnLeft': { 2366 $this->LayoutMode = 'TwoColumnLeft'; 2367 break; 2368 } 2369 case 'TwoColumnRight': { 2370 $this->LayoutMode = 'TwoColumnRight'; 2371 break; 2372 } 2373 case 'TwoPageLeft': { 2374 $this->LayoutMode = 'TwoPageLeft'; 2375 break; 2376 } 2377 case 'TwoPageRight': { 2378 $this->LayoutMode = 'TwoPageRight'; 2379 break; 2380 } 2381 default: { 2382 $this->LayoutMode = 'SinglePage'; 2383 } 2384 } 2385 switch ($mode) { 2386 case 'UseNone': { 2387 $this->PageMode = 'UseNone'; 2388 break; 2389 } 2390 case 'UseOutlines': { 2391 $this->PageMode = 'UseOutlines'; 2392 break; 2393 } 2394 case 'UseThumbs': { 2395 $this->PageMode = 'UseThumbs'; 2396 break; 2397 } 2398 case 'FullScreen': { 2399 $this->PageMode = 'FullScreen'; 2400 break; 2401 } 2402 case 'UseOC': { 2403 $this->PageMode = 'UseOC'; 2404 break; 2405 } 2406 case '': { 2407 $this->PageMode = 'UseAttachments'; 2408 break; 2409 } 2410 default: { 2411 $this->PageMode = 'UseNone'; 2412 } 2413 } 2414 } 2415 2416 /** 2417 * Activates or deactivates page compression. When activated, the internal representation of each page is compressed, which leads to a compression ratio of about 2 for the resulting document. Compression is on by default. 2418 * Note: the Zlib extension is required for this feature. If not present, compression will be turned off. 2419 * @param boolean $compress Boolean indicating if compression must be enabled. 2420 * @access public 2421 * @since 1.4 2422 */ 2423 public function SetCompression($compress) { 2424 //Set page compression 2425 if (function_exists('gzcompress')) { 2426 $this->compress = $compress; 2427 } else { 2428 $this->compress = false; 2429 } 2430 } 2431 2432 /** 2433 * Defines the title of the document. 2434 * @param string $title The title. 2435 * @access public 2436 * @since 1.2 2437 * @see SetAuthor(), SetCreator(), SetKeywords(), SetSubject() 2438 */ 2439 public function SetTitle($title) { 2440 //Title of document 2441 $this->title = $title; 2442 } 2443 2444 /** 2445 * Defines the subject of the document. 2446 * @param string $subject The subject. 2447 * @access public 2448 * @since 1.2 2449 * @see SetAuthor(), SetCreator(), SetKeywords(), SetTitle() 2450 */ 2451 public function SetSubject($subject) { 2452 //Subject of document 2453 $this->subject = $subject; 2454 } 2455 2456 /** 2457 * Defines the author of the document. 2458 * @param string $author The name of the author. 2459 * @access public 2460 * @since 1.2 2461 * @see SetCreator(), SetKeywords(), SetSubject(), SetTitle() 2462 */ 2463 public function SetAuthor($author) { 2464 //Author of document 2465 $this->author = $author; 2466 } 2467 2468 /** 2469 * Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'. 2470 * @param string $keywords The list of keywords. 2471 * @access public 2472 * @since 1.2 2473 * @see SetAuthor(), SetCreator(), SetSubject(), SetTitle() 2474 */ 2475 public function SetKeywords($keywords) { 2476 //Keywords of document 2477 $this->keywords = $keywords; 2478 } 2479 2480 /** 2481 * Defines the creator of the document. This is typically the name of the application that generates the PDF. 2482 * @param string $creator The name of the creator. 2483 * @access public 2484 * @since 1.2 2485 * @see SetAuthor(), SetKeywords(), SetSubject(), SetTitle() 2486 */ 2487 public function SetCreator($creator) { 2488 //Creator of document 2489 $this->creator = $creator; 2490 } 2491 2492 /** 2493 * This method is automatically called in case of fatal error; it simply outputs the message and halts the execution. An inherited class may override it to customize the error handling but should always halt the script, or the resulting document would probably be invalid. 2494 * 2004-06-11 :: Nicola Asuni : changed bold tag with strong 2495 * @param string $msg The error message 2496 * @access public 2497 * @since 1.0 2498 */ 2499 public function Error($msg) { 2500 // unset all class variables 2501 $this->_destroy(true); 2502 // exit program and print error 2503 die('<strong>TCPDF ERROR: </strong>'.$msg); 2504 } 2505 2506 /** 2507 * This method begins the generation of the PDF document. 2508 * It is not necessary to call it explicitly because AddPage() does it automatically. 2509 * Note: no page is created by this method 2510 * @access public 2511 * @since 1.0 2512 * @see AddPage(), Close() 2513 */ 2514 public function Open() { 2515 //Begin document 2516 $this->state = 1; 2517 } 2518 2519 /** 2520 * Terminates the PDF document. 2521 * It is not necessary to call this method explicitly because Output() does it automatically. 2522 * If the document contains no page, AddPage() is called to prevent from getting an invalid document. 2523 * @access public 2524 * @since 1.0 2525 * @see Open(), Output() 2526 */ 2527 public function Close() { 2528 if ($this->state == 3) { 2529 return; 2530 } 2531 if ($this->page == 0) { 2532 $this->AddPage(); 2533 } 2534 // close page 2535 $this->endPage(); 2536 // close document 2537 $this->_enddoc(); 2538 // unset all class variables (except critical ones) 2539 $this->_destroy(false); 2540 } 2541 2542 /** 2543 * Move pointer at the specified document page and update page dimensions. 2544 * @param int $pnum page number (1 ... numpages) 2545 * @param boolean $resetmargins if true reset left, right, top margins and Y position. 2546 * @access public 2547 * @since 2.1.000 (2008-01-07) 2548 * @see getPage(), lastpage(), getNumPages() 2549 */ 2550 public function setPage($pnum, $resetmargins=false) { 2551 if ($pnum == $this->page) { 2552 return; 2553 } 2554 if (($pnum > 0) AND ($pnum <= $this->numpages)) { 2555 $this->state = 2; 2556 // save current graphic settings 2557 //$gvars = $this->getGraphicVars(); 2558 $oldpage = $this->page; 2559 $this->page = $pnum; 2560 $this->wPt = $this->pagedim[$this->page]['w']; 2561 $this->hPt = $this->pagedim[$this->page]['h']; 2562 $this->w = $this->wPt / $this->k; 2563 $this->h = $this->hPt / $this->k; 2564 $this->tMargin = $this->pagedim[$this->page]['tm']; 2565 $this->bMargin = $this->pagedim[$this->page]['bm']; 2566 $this->original_lMargin = $this->pagedim[$this->page]['olm']; 2567 $this->original_rMargin = $this->pagedim[$this->page]['orm']; 2568 $this->AutoPageBreak = $this->pagedim[$this->page]['pb']; 2569 $this->CurOrientation = $this->pagedim[$this->page]['or']; 2570 $this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin); 2571 // restore graphic settings 2572 //$this->setGraphicVars($gvars); 2573 if ($resetmargins) { 2574 $this->lMargin = $this->pagedim[$this->page]['olm']; 2575 $this->rMargin = $this->pagedim[$this->page]['orm']; 2576 $this->SetY($this->tMargin); 2577 } else { 2578 // account for booklet mode 2579 if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) { 2580 $deltam = $this->pagedim[$this->page]['olm'] - $this->pagedim[$this->page]['orm']; 2581 $this->lMargin += $deltam; 2582 $this->rMargin -= $deltam; 2583 } 2584 } 2585 } else { 2586 $this->Error('Wrong page number on setPage() function.'); 2587 } 2588 } 2589 2590 /** 2591 * Reset pointer to the last document page. 2592 * @param boolean $resetmargins if true reset left, right, top margins and Y position. 2593 * @access public 2594 * @since 2.0.000 (2008-01-04) 2595 * @see setPage(), getPage(), getNumPages() 2596 */ 2597 public function lastPage($resetmargins=false) { 2598 $this->setPage($this->getNumPages(), $resetmargins); 2599 } 2600 2601 /** 2602 * Get current document page number. 2603 * @return int page number 2604 * @access public 2605 * @since 2.1.000 (2008-01-07) 2606 * @see setPage(), lastpage(), getNumPages() 2607 */ 2608 public function getPage() { 2609 return $this->page; 2610 } 2611 2612 2613 /** 2614 * Get the total number of insered pages. 2615 * @return int number of pages 2616 * @access public 2617 * @since 2.1.000 (2008-01-07) 2618 * @see setPage(), getPage(), lastpage() 2619 */ 2620 public function getNumPages() { 2621 return $this->numpages; 2622 } 2623 2624 /** 2625 * Adds a new TOC (Table Of Content) page to the document. 2626 * @param string $orientation page orientation. 2627 * @param boolean $keepmargins if true overwrites the default page margins with the current margins 2628 * @access public 2629 * @since 5.0.001 (2010-05-06) 2630 * @see AddPage(), startPage(), endPage(), endTOCPage() 2631 */ 2632 public function addTOCPage($orientation='', $format='', $keepmargins=false) { 2633 $this->AddPage($orientation, $format, $keepmargins, true); 2634 } 2635 2636 /** 2637 * Terminate the current TOC (Table Of Content) page 2638 * @access public 2639 * @since 5.0.001 (2010-05-06) 2640 * @see AddPage(), startPage(), endPage(), addTOCPage() 2641 */ 2642 public function endTOCPage() { 2643 $this->endPage(true); 2644 } 2645 2646 /** 2647 * Adds a new page to the document. If a page is already present, the Footer() method is called first to output the footer (if enabled). Then the page is added, the current position set to the top-left corner according to the left and top margins (or top-right if in RTL mode), and Header() is called to display the header (if enabled). 2648 * The origin of the coordinate system is at the top-left corner (or top-right for RTL) and increasing ordinates go downwards. 2649 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul> 2650 * @param mixed $format The format used for pages. It can be either one of the following values (case insensitive) or a custom format in the form of a two-element array containing the width and the height (expressed in the unit given by unit).<ul><li>4A0</li><li>2A0</li><li>A0</li><li>A1</li><li>A2</li><li>A3</li><li>A4 (default)</li><li>A5</li><li>A6</li><li>A7</li><li>A8</li><li>A9</li><li>A10</li><li>B0</li><li>B1</li><li>B2</li><li>B3</li><li>B4</li><li>B5</li><li>B6</li><li>B7</li><li>B8</li><li>B9</li><li>B10</li><li>C0</li><li>C1</li><li>C2</li><li>C3</li><li>C4</li><li>C5</li><li>C6</li><li>C7</li><li>C8</li><li>C9</li><li>C10</li><li>RA0</li><li>RA1</li><li>RA2</li><li>RA3</li><li>RA4</li><li>SRA0</li><li>SRA1</li><li>SRA2</li><li>SRA3</li><li>SRA4</li><li>LETTER</li><li>LEGAL</li><li>EXECUTIVE</li><li>FOLIO</li></ul> 2651 * @param boolean $keepmargins if true overwrites the default page margins with the current margins 2652 * @param boolean $tocpage if true set the tocpage state to true (the added page will be used to display Table Of Content). 2653 * @access public 2654 * @since 1.0 2655 * @see startPage(), endPage(), addTOCPage(), endTOCPage() 2656 */ 2657 public function AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false) { 2658 if (!isset($this->original_lMargin) OR $keepmargins) { 2659 $this->original_lMargin = $this->lMargin; 2660 } 2661 if (!isset($this->original_rMargin) OR $keepmargins) { 2662 $this->original_rMargin = $this->rMargin; 2663 } 2664 // terminate previous page 2665 $this->endPage(); 2666 // start new page 2667 $this->startPage($orientation, $format, $tocpage); 2668 } 2669 2670 /** 2671 * Terminate the current page 2672 * @param boolean $tocpage if true set the tocpage state to false (end the page used to display Table Of Content). 2673 * @access public 2674 * @since 4.2.010 (2008-11-14) 2675 * @see AddPage(), startPage(), addTOCPage(), endTOCPage() 2676 */ 2677 public function endPage($tocpage=false) { 2678 // check if page is already closed 2679 if (($this->page == 0) OR ($this->numpages > $this->page) OR (!$this->pageopen[$this->page])) { 2680 return; 2681 } 2682 $this->InFooter = true; 2683 // print page footer 2684 $this->setFooter(); 2685 // close page 2686 $this->_endpage(); 2687 // mark page as closed 2688 $this->pageopen[$this->page] = false; 2689 $this->InFooter = false; 2690 if ($tocpage) { 2691 $this->tocpage = false; 2692 } 2693 } 2694 2695 /** 2696 * Starts a new page to the document. The page must be closed using the endPage() function. 2697 * The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards. 2698 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul> 2699 * @param mixed $format The format used for pages. It can be either one of the following values (case insensitive) or a custom format in the form of a two-element array containing the width and the height (expressed in the unit given by unit).<ul><li>4A0</li><li>2A0</li><li>A0</li><li>A1</li><li>A2</li><li>A3</li><li>A4 (default)</li><li>A5</li><li>A6</li><li>A7</li><li>A8</li><li>A9</li><li>A10</li><li>B0</li><li>B1</li><li>B2</li><li>B3</li><li>B4</li><li>B5</li><li>B6</li><li>B7</li><li>B8</li><li>B9</li><li>B10</li><li>C0</li><li>C1</li><li>C2</li><li>C3</li><li>C4</li><li>C5</li><li>C6</li><li>C7</li><li>C8</li><li>C9</li><li>C10</li><li>RA0</li><li>RA1</li><li>RA2</li><li>RA3</li><li>RA4</li><li>SRA0</li><li>SRA1</li><li>SRA2</li><li>SRA3</li><li>SRA4</li><li>LETTER</li><li>LEGAL</li><li>EXECUTIVE</li><li>FOLIO</li></ul> 2700 * @param boolean $tocpage if true set the tocpage state to true (the added page will be used to display Table of Content). 2701 * @access public 2702 * @since 4.2.010 (2008-11-14) 2703 * @see AddPage(), endPage(), addTOCPage(), endTOCPage() 2704 */ 2705 public function startPage($orientation='', $format='', $tocpage=false) { 2706 if ($tocpage) { 2707 $this->tocpage = true; 2708 } 2709 if ($this->numpages > $this->page) { 2710 // this page has been already added 2711 $this->setPage($this->page + 1); 2712 $this->SetY($this->tMargin); 2713 return; 2714 } 2715 // start a new page 2716 if ($this->state == 0) { 2717 $this->Open(); 2718 } 2719 ++$this->numpages; 2720 $this->swapMargins($this->booklet); 2721 // save current graphic settings 2722 $gvars = $this->getGraphicVars(); 2723 // start new page 2724 $this->_beginpage($orientation, $format); 2725 // mark page as open 2726 $this->pageopen[$this->page] = true; 2727 // restore graphic settings 2728 $this->setGraphicVars($gvars); 2729 // mark this point 2730 $this->setPageMark(); 2731 // print page header 2732 $this->setHeader(); 2733 // restore graphic settings 2734 $this->setGraphicVars($gvars); 2735 // mark this point 2736 $this->setPageMark(); 2737 // print table header (if any) 2738 $this->setTableHeader(); 2739 } 2740 2741 /** 2742 * Set start-writing mark on current page stream used to put borders and fills. 2743 * Borders and fills are always created after content and inserted on the position marked by this method. 2744 * This function must be called after calling Image() function for a background image. 2745 * Background images must be always inserted before calling Multicell() or WriteHTMLCell() or WriteHTML() functions. 2746 * @access public 2747 * @since 4.0.016 (2008-07-30) 2748 */ 2749 public function setPageMark() { 2750 $this->intmrk[$this->page] = $this->pagelen[$this->page]; 2751 $this->setContentMark(); 2752 } 2753 2754 /** 2755 * Set start-writing mark on selected page. 2756 * Borders and fills are always created after content and inserted on the position marked by this method. 2757 * @param int $page page number (default is the current page) 2758 * @access protected 2759 * @since 4.6.021 (2009-07-20) 2760 */ 2761 protected function setContentMark($page=0) { 2762 if ($page <= 0) { 2763 $page = $this->page; 2764 } 2765 if (isset($this->footerlen[$page])) { 2766 $this->cntmrk[$page] = $this->pagelen[$page] - $this->footerlen[$page]; 2767 } else { 2768 $this->cntmrk[$page] = $this->pagelen[$page]; 2769 } 2770 } 2771 2772 /** 2773 * Set header data. 2774 * @param string $ln header image logo 2775 * @param string $lw header image logo width in mm 2776 * @param string $ht string to print as title on document header 2777 * @param string $hs string to print on document header 2778 * @access public 2779 */ 2780 public function setHeaderData($ln='', $lw=0, $ht='', $hs='') { 2781 $this->header_logo = $ln; 2782 $this->header_logo_width = $lw; 2783 $this->header_title = $ht; 2784 $this->header_string = $hs; 2785 } 2786 2787 /** 2788 * Returns header data: 2789 * <ul><li>$ret['logo'] = logo image</li><li>$ret['logo_width'] = width of the image logo in user units</li><li>$ret['title'] = header title</li><li>$ret['string'] = header description string</li></ul> 2790 * @return array() 2791 * @access public 2792 * @since 4.0.012 (2008-07-24) 2793 */ 2794 public function getHeaderData() { 2795 $ret = array(); 2796 $ret['logo'] = $this->header_logo; 2797 $ret['logo_width'] = $this->header_logo_width; 2798 $ret['title'] = $this->header_title; 2799 $ret['string'] = $this->header_string; 2800 return $ret; 2801 } 2802 2803 /** 2804 * Set header margin. 2805 * (minimum distance between header and top page margin) 2806 * @param int $hm distance in user units 2807 * @access public 2808 */ 2809 public function setHeaderMargin($hm=10) { 2810 $this->header_margin = $hm; 2811 } 2812 2813 /** 2814 * Returns header margin in user units. 2815 * @return float 2816 * @since 4.0.012 (2008-07-24) 2817 * @access public 2818 */ 2819 public function getHeaderMargin() { 2820 return $this->header_margin; 2821 } 2822 2823 /** 2824 * Set footer margin. 2825 * (minimum distance between footer and bottom page margin) 2826 * @param int $fm distance in user units 2827 * @access public 2828 */ 2829 public function setFooterMargin($fm=10) { 2830 $this->footer_margin = $fm; 2831 } 2832 2833 /** 2834 * Returns footer margin in user units. 2835 * @return float 2836 * @since 4.0.012 (2008-07-24) 2837 * @access public 2838 */ 2839 public function getFooterMargin() { 2840 return $this->footer_margin; 2841 } 2842 /** 2843 * Set a flag to print page header. 2844 * @param boolean $val set to true to print the page header (default), false otherwise. 2845 * @access public 2846 */ 2847 public function setPrintHeader($val=true) { 2848 $this->print_header = $val; 2849 } 2850 2851 /** 2852 * Set a flag to print page footer. 2853 * @param boolean $value set to true to print the page footer (default), false otherwise. 2854 * @access public 2855 */ 2856 public function setPrintFooter($val=true) { 2857 $this->print_footer = $val; 2858 } 2859 2860 /** 2861 * Return the right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image 2862 * @return float 2863 * @access public 2864 */ 2865 public function getImageRBX() { 2866 return $this->img_rb_x; 2867 } 2868 2869 /** 2870 * Return the right-bottom (or left-bottom for RTL) corner Y coordinate of last inserted image 2871 * @return float 2872 * @access public 2873 */ 2874 public function getImageRBY() { 2875 return $this->img_rb_y; 2876 } 2877 2878 /** 2879 * This method is used to render the page header. 2880 * It is automatically called by AddPage() and could be overwritten in your own inherited class. 2881 * @access public 2882 */ 2883 public function Header() { 2884 $ormargins = $this->getOriginalMargins(); 2885 $headerfont = $this->getHeaderFont(); 2886 $headerdata = $this->getHeaderData(); 2887 if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) { 2888 $this->Image(K_PATH_IMAGES.$headerdata['logo'], $this->GetX(), $this->getHeaderMargin(), $headerdata['logo_width']); 2889 $imgy = $this->getImageRBY(); 2890 } else { 2891 $imgy = $this->GetY(); 2892 } 2893 $cell_height = round(($this->getCellHeightRatio() * $headerfont[2]) / $this->getScaleFactor(), 2); 2894 // set starting margin for text data cell 2895 if ($this->getRTL()) { 2896 $header_x = $ormargins['right'] + ($headerdata['logo_width'] * 1.1); 2897 } else { 2898 $header_x = $ormargins['left'] + ($headerdata['logo_width'] * 1.1); 2899 } 2900 $this->SetTextColor(0, 0, 0); 2901 // header title 2902 $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1); 2903 $this->SetX($header_x); 2904 $this->Cell(0, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0); 2905 // header string 2906 $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]); 2907 $this->SetX($header_x); 2908 $this->MultiCell(0, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false); 2909 // print an ending header line 2910 $this->SetLineStyle(array('width' => 0.85 / $this->getScaleFactor(), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))); 2911 $this->SetY((2.835 / $this->getScaleFactor()) + max($imgy, $this->GetY())); 2912 if ($this->getRTL()) { 2913 $this->SetX($ormargins['right']); 2914 } else { 2915 $this->SetX($ormargins['left']); 2916 } 2917 $this->Cell(0, 0, '', 'T', 0, 'C'); 2918 } 2919 2920 /** 2921 * This method is used to render the page footer. 2922 * It is automatically called by AddPage() and could be overwritten in your own inherited class. 2923 * @access public 2924 */ 2925 public function Footer() { 2926 $cur_y = $this->GetY(); 2927 $ormargins = $this->getOriginalMargins(); 2928 $this->SetTextColor(0, 0, 0); 2929 //set style for cell border 2930 $line_width = 0.85 / $this->getScaleFactor(); 2931 $this->SetLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))); 2932 //print document barcode 2933 $barcode = $this->getBarcode(); 2934 if (!empty($barcode)) { 2935 $this->Ln($line_width); 2936 $barcode_width = round(($this->getPageWidth() - $ormargins['left'] - $ormargins['right'])/3); 2937 $this->write1DBarcode($barcode, 'C128B', $this->GetX(), $cur_y + $line_width, $barcode_width, (($this->getFooterMargin() / 3) - $line_width), 0.3, '', ''); 2938 } 2939 if (empty($this->pagegroups)) { 2940 $pagenumtxt = $this->l['w_page'].' '.$this->getAliasNumPage().' / '.$this->getAliasNbPages(); 2941 } else { 2942 $pagenumtxt = $this->l['w_page'].' '.$this->getPageNumGroupAlias().' / '.$this->getPageGroupAlias(); 2943 } 2944 $this->SetY($cur_y); 2945 //Print page number 2946 if ($this->getRTL()) { 2947 $this->SetX($ormargins['right']); 2948 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L'); 2949 } else { 2950 $this->SetX($ormargins['left']); 2951 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'R'); 2952 } 2953 } 2954 2955 /** 2956 * This method is used to render the page header. 2957 * @access protected 2958 * @since 4.0.012 (2008-07-24) 2959 */ 2960 protected function setHeader() { 2961 if ($this->print_header) { 2962 $temp_thead = $this->thead; 2963 $temp_theadMargins = $this->theadMargins; 2964 $lasth = $this->lasth; 2965 $this->_out('q'); 2966 $this->rMargin = $this->original_rMargin; 2967 $this->lMargin = $this->original_lMargin; 2968 $this->cMargin = 0; 2969 //set current position 2970 if ($this->rtl) { 2971 $this->SetXY($this->original_rMargin, $this->header_margin); 2972 } else { 2973 $this->SetXY($this->original_lMargin, $this->header_margin); 2974 } 2975 $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]); 2976 $this->Header(); 2977 //restore position 2978 if ($this->rtl) { 2979 $this->SetXY($this->original_rMargin, $this->tMargin); 2980 } else { 2981 $this->SetXY($this->original_lMargin, $this->tMargin); 2982 } 2983 $this->_out('Q'); 2984 $this->lasth = $lasth; 2985 $this->thead = $temp_thead; 2986 $this->theadMargins = $temp_theadMargins; 2987 $this->newline = false; 2988 } 2989 } 2990 2991 /** 2992 * This method is used to render the page footer. 2993 * @access protected 2994 * @since 4.0.012 (2008-07-24) 2995 */ 2996 protected function setFooter() { 2997 //Page footer 2998 // save current graphic settings 2999 $gvars = $this->getGraphicVars(); 3000 // mark this point 3001 $this->footerpos[$this->page] = $this->pagelen[$this->page]; 3002 $this->_out("\n"); 3003 if ($this->print_footer) { 3004 $temp_thead = $this->thead; 3005 $temp_theadMargins = $this->theadMargins; 3006 $lasth = $this->lasth; 3007 $this->_out('q'); 3008 $this->rMargin = $this->original_rMargin; 3009 $this->lMargin = $this->original_lMargin; 3010 $this->cMargin = 0; 3011 //set current position 3012 $footer_y = $this->h - $this->footer_margin; 3013 if ($this->rtl) { 3014 $this->SetXY($this->original_rMargin, $footer_y); 3015 } else { 3016 $this->SetXY($this->original_lMargin, $footer_y); 3017 } 3018 $this->SetFont($this->footer_font[0], $this->footer_font[1], $this->footer_font[2]); 3019 $this->Footer(); 3020 //restore position 3021 if ($this->rtl) { 3022 $this->SetXY($this->original_rMargin, $this->tMargin); 3023 } else { 3024 $this->SetXY($this->original_lMargin, $this->tMargin); 3025 } 3026 $this->_out('Q'); 3027 $this->lasth = $lasth; 3028 $this->thead = $temp_thead; 3029 $this->theadMargins = $temp_theadMargins; 3030 } 3031 // restore graphic settings 3032 $this->setGraphicVars($gvars); 3033 // calculate footer length 3034 $this->footerlen[$this->page] = $this->pagelen[$this->page] - $this->footerpos[$this->page] + 1; 3035 } 3036 3037 /** 3038 * This method is used to render the table header on new page (if any). 3039 * @access protected 3040 * @since 4.5.030 (2009-03-25) 3041 */ 3042 protected function setTableHeader() { 3043 if ($this->num_columns > 1) { 3044 // multi column mode 3045 return; 3046 } 3047 if (isset($this->theadMargins['top'])) { 3048 // restore the original top-margin 3049 $this->tMargin = $this->theadMargins['top']; 3050 $this->pagedim[$this->page]['tm'] = $this->tMargin; 3051 $this->y = $this->tMargin; 3052 } 3053 if (!$this->empty_string($this->thead) AND (!$this->inthead)) { 3054 // set margins 3055 $prev_lMargin = $this->lMargin; 3056 $prev_rMargin = $this->rMargin; 3057 $this->lMargin = $this->pagedim[$this->page]['olm']; 3058 $this->rMargin = $this->pagedim[$this->page]['orm']; 3059 $this->cMargin = $this->theadMargins['cmargin']; 3060 if ($this->rtl) { 3061 $this->x = $this->w - $this->rMargin; 3062 } else { 3063 $this->x = $this->lMargin; 3064 } 3065 // print table header 3066 $this->writeHTML($this->thead, false, false, false, false, ''); 3067 // set new top margin to skip the table headers 3068 if (!isset($this->theadMargins['top'])) { 3069 $this->theadMargins['top'] = $this->tMargin; 3070 } 3071 $this->tMargin = $this->y; 3072 $this->pagedim[$this->page]['tm'] = $this->tMargin; 3073 $this->lasth = 0; 3074 $this->lMargin = $prev_lMargin; 3075 $this->rMargin = $prev_rMargin; 3076 } 3077 } 3078 3079 /** 3080 * Returns the current page number. 3081 * @return int page number 3082 * @access public 3083 * @since 1.0 3084 * @see AliasNbPages(), getAliasNbPages() 3085 */ 3086 public function PageNo() { 3087 return $this->page; 3088 } 3089 3090 /** 3091 * Defines a new spot color. 3092 * It can be expressed in RGB components or gray scale. 3093 * The method can be called before the first page is created and the value is retained from page to page. 3094 * @param int $c Cyan color for CMYK. Value between 0 and 255 3095 * @param int $m Magenta color for CMYK. Value between 0 and 255 3096 * @param int $y Yellow color for CMYK. Value between 0 and 255 3097 * @param int $k Key (Black) color for CMYK. Value between 0 and 255 3098 * @access public 3099 * @since 4.0.024 (2008-09-12) 3100 * @see SetDrawSpotColor(), SetFillSpotColor(), SetTextSpotColor() 3101 */ 3102 public function AddSpotColor($name, $c, $m, $y, $k) { 3103 if (!isset($this->spot_colors[$name])) { 3104 $i = 1 + count($this->spot_colors); 3105 $this->spot_colors[$name] = array('i' => $i, 'c' => $c, 'm' => $m, 'y' => $y, 'k' => $k); 3106 } 3107 } 3108 3109 /** 3110 * Defines the color used for all drawing operations (lines, rectangles and cell borders). 3111 * It can be expressed in RGB components or gray scale. 3112 * The method can be called before the first page is created and the value is retained from page to page. 3113 * @param array $color array of colors 3114 * @access public 3115 * @since 3.1.000 (2008-06-11) 3116 * @see SetDrawColor() 3117 */ 3118 public function SetDrawColorArray($color) { 3119 if (isset($color)) { 3120 $color = array_values($color); 3121 $r = isset($color[0]) ? $color[0] : -1; 3122 $g = isset($color[1]) ? $color[1] : -1; 3123 $b = isset($color[2]) ? $color[2] : -1; 3124 $k = isset($color[3]) ? $color[3] : -1; 3125 if ($r >= 0) { 3126 $this->SetDrawColor($r, $g, $b, $k); 3127 } 3128 } 3129 } 3130 3131 /** 3132 * Defines the color used for all drawing operations (lines, rectangles and cell borders). It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page. 3133 * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255 3134 * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255 3135 * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255 3136 * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255 3137 * @access public 3138 * @since 1.3 3139 * @see SetDrawColorArray(), SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell() 3140 */ 3141 public function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1) { 3142 // set default values 3143 if (!is_numeric($col1)) { 3144 $col1 = 0; 3145 } 3146 if (!is_numeric($col2)) { 3147 $col2 = -1; 3148 } 3149 if (!is_numeric($col3)) { 3150 $col3 = -1; 3151 } 3152 if (!is_numeric($col4)) { 3153 $col4 = -1; 3154 } 3155 //Set color for all stroking operations 3156 if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) { 3157 // Grey scale 3158 $this->DrawColor = sprintf('%.3F G', $col1/255); 3159 $this->strokecolor = array('G' => $col1); 3160 } elseif ($col4 == -1) { 3161 // RGB 3162 $this->DrawColor = sprintf('%.3F %.3F %.3F RG', $col1/255, $col2/255, $col3/255); 3163 $this->strokecolor = array('R' => $col1, 'G' => $col2, 'B' => $col3); 3164 } else { 3165 // CMYK 3166 $this->DrawColor = sprintf('%.3F %.3F %.3F %.3F K', $col1/100, $col2/100, $col3/100, $col4/100); 3167 $this->strokecolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4); 3168 } 3169 if ($this->page > 0) { 3170 $this->_out($this->DrawColor); 3171 } 3172 } 3173 3174 /** 3175 * Defines the spot color used for all drawing operations (lines, rectangles and cell borders). 3176 * @param string $name name of the spot color 3177 * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default). 3178 * @access public 3179 * @since 4.0.024 (2008-09-12) 3180 * @see AddSpotColor(), SetFillSpotColor(), SetTextSpotColor() 3181 */ 3182 public function SetDrawSpotColor($name, $tint=100) { 3183 if (!isset($this->spot_colors[$name])) { 3184 $this->Error('Undefined spot color: '.$name); 3185 } 3186 $this->DrawColor = sprintf('/CS%d CS %.3F SCN', $this->spot_colors[$name]['i'], $tint/100); 3187 if ($this->page > 0) { 3188 $this->_out($this->DrawColor); 3189 } 3190 } 3191 3192 /** 3193 * Defines the color used for all filling operations (filled rectangles and cell backgrounds). 3194 * It can be expressed in RGB components or gray scale. 3195 * The method can be called before the first page is created and the value is retained from page to page. 3196 * @param array $color array of colors 3197 * @access public 3198 * @since 3.1.000 (2008-6-11) 3199 * @see SetFillColor() 3200 */ 3201 public function SetFillColorArray($color) { 3202 if (isset($color)) { 3203 $color = array_values($color); 3204 $r = isset($color[0]) ? $color[0] : -1; 3205 $g = isset($color[1]) ? $color[1] : -1; 3206 $b = isset($color[2]) ? $color[2] : -1; 3207 $k = isset($color[3]) ? $color[3] : -1; 3208 if ($r >= 0) { 3209 $this->SetFillColor($r, $g, $b, $k); 3210 } 3211 } 3212 } 3213 3214 /** 3215 * Defines the color used for all filling operations (filled rectangles and cell backgrounds). It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page. 3216 * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255 3217 * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255 3218 * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255 3219 * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255 3220 * @access public 3221 * @since 1.3 3222 * @see SetFillColorArray(), SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell() 3223 */ 3224 public function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1) { 3225 // set default values 3226 if (!is_numeric($col1)) { 3227 $col1 = 0; 3228 } 3229 if (!is_numeric($col2)) { 3230 $col2 = -1; 3231 } 3232 if (!is_numeric($col3)) { 3233 $col3 = -1; 3234 } 3235 if (!is_numeric($col4)) { 3236 $col4 = -1; 3237 } 3238 //Set color for all filling operations 3239 if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) { 3240 // Grey scale 3241 $this->FillColor = sprintf('%.3F g', $col1/255); 3242 $this->bgcolor = array('G' => $col1); 3243 } elseif ($col4 == -1) { 3244 // RGB 3245 $this->FillColor = sprintf('%.3F %.3F %.3F rg', $col1/255, $col2/255, $col3/255); 3246 $this->bgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3); 3247 } else { 3248 // CMYK 3249 $this->FillColor = sprintf('%.3F %.3F %.3F %.3F k', $col1/100, $col2/100, $col3/100, $col4/100); 3250 $this->bgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4); 3251 } 3252 $this->ColorFlag = ($this->FillColor != $this->TextColor); 3253 if ($this->page > 0) { 3254 $this->_out($this->FillColor); 3255 } 3256 } 3257 3258 /** 3259 * Defines the spot color used for all filling operations (filled rectangles and cell backgrounds). 3260 * @param string $name name of the spot color 3261 * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default). 3262 * @access public 3263 * @since 4.0.024 (2008-09-12) 3264 * @see AddSpotColor(), SetDrawSpotColor(), SetTextSpotColor() 3265 */ 3266 public function SetFillSpotColor($name, $tint=100) { 3267 if (!isset($this->spot_colors[$name])) { 3268 $this->Error('Undefined spot color: '.$name); 3269 } 3270 $this->FillColor = sprintf('/CS%d cs %.3F scn', $this->spot_colors[$name]['i'], $tint/100); 3271 $this->ColorFlag = ($this->FillColor != $this->TextColor); 3272 if ($this->page > 0) { 3273 $this->_out($this->FillColor); 3274 } 3275 } 3276 3277 /** 3278 * Defines the color used for text. It can be expressed in RGB components or gray scale. 3279 * The method can be called before the first page is created and the value is retained from page to page. 3280 * @param array $color array of colors 3281 * @access public 3282 * @since 3.1.000 (2008-6-11) 3283 * @see SetFillColor() 3284 */ 3285 public function SetTextColorArray($color) { 3286 if (isset($color)) { 3287 $color = array_values($color); 3288 $r = isset($color[0]) ? $color[0] : -1; 3289 $g = isset($color[1]) ? $color[1] : -1; 3290 $b = isset($color[2]) ? $color[2] : -1; 3291 $k = isset($color[3]) ? $color[3] : -1; 3292 if ($r >= 0) { 3293 $this->SetTextColor($r, $g, $b, $k); 3294 } 3295 } 3296 } 3297 3298 /** 3299 * Defines the color used for text. It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page. 3300 * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255 3301 * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255 3302 * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255 3303 * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255 3304 * @access public 3305 * @since 1.3 3306 * @see SetTextColorArray(), SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell() 3307 */ 3308 public function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1) { 3309 // set default values 3310 if (!is_numeric($col1)) { 3311 $col1 = 0; 3312 } 3313 if (!is_numeric($col2)) { 3314 $col2 = -1; 3315 } 3316 if (!is_numeric($col3)) { 3317 $col3 = -1; 3318 } 3319 if (!is_numeric($col4)) { 3320 $col4 = -1; 3321 } 3322 //Set color for text 3323 if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) { 3324 // Grey scale 3325 $this->TextColor = sprintf('%.3F g', $col1/255); 3326 $this->fgcolor = array('G' => $col1); 3327 } elseif ($col4 == -1) { 3328 // RGB 3329 $this->TextColor = sprintf('%.3F %.3F %.3F rg', $col1/255, $col2/255, $col3/255); 3330 $this->fgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3); 3331 } else { 3332 // CMYK 3333 $this->TextColor = sprintf('%.3F %.3F %.3F %.3F k', $col1/100, $col2/100, $col3/100, $col4/100); 3334 $this->fgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4); 3335 } 3336 $this->ColorFlag = ($this->FillColor != $this->TextColor); 3337 } 3338 3339 /** 3340 * Defines the spot color used for text. 3341 * @param string $name name of the spot color 3342 * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default). 3343 * @access public 3344 * @since 4.0.024 (2008-09-12) 3345 * @see AddSpotColor(), SetDrawSpotColor(), SetFillSpotColor() 3346 */ 3347 public function SetTextSpotColor($name, $tint=100) { 3348 if (!isset($this->spot_colors[$name])) { 3349 $this->Error('Undefined spot color: '.$name); 3350 } 3351 $this->TextColor = sprintf('/CS%d cs %.3F scn', $this->spot_colors[$name]['i'], $tint/100); 3352 $this->ColorFlag = ($this->FillColor != $this->TextColor); 3353 if ($this->page > 0) { 3354 $this->_out($this->TextColor); 3355 } 3356 } 3357 3358 /** 3359 * Returns the length of a string in user unit. A font must be selected.<br> 3360 * @param string $s The string whose length is to be computed 3361 * @param string $fontname Family font. It can be either a name defined by AddFont() or one of the standard families. It is also possible to pass an empty string, in that case, the current family is retained. 3362 * @param string $fontstyle Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line-trough</li><li>O: overline</li></ul> or any combination. The default value is regular. 3363 * @param float $fontsize Font size in points. The default value is the current size. 3364 * @param boolean $getarray if true returns an array of characters widths, if false returns the total length. 3365 * @return mixed int total string length or array of characted widths 3366 * @author Nicola Asuni 3367 * @access public 3368 * @since 1.2 3369 */ 3370 public function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) { 3371 return $this->GetArrStringWidth($this->utf8Bidi($this->UTF8StringToArray($s), $s, $this->tmprtl), $fontname, $fontstyle, $fontsize, $getarray); 3372 } 3373 3374 /** 3375 * Returns the string length of an array of chars in user unit or an array of characters widths. A font must be selected.<br> 3376 * @param string $sa The array of chars whose total length is to be computed 3377 * @param string $fontname Family font. It can be either a name defined by AddFont() or one of the standard families. It is also possible to pass an empty string, in that case, the current family is retained. 3378 * @param string $fontstyle Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line trough</li><li>O: overline</li></ul> or any combination. The default value is regular. 3379 * @param float $fontsize Font size in points. The default value is the current size. 3380 * @param boolean $getarray if true returns an array of characters widths, if false returns the total length. 3381 * @return mixed int total string length or array of characted widths 3382 * @author Nicola Asuni 3383 * @access public 3384 * @since 2.4.000 (2008-03-06) 3385 */ 3386 public function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) { 3387 // store current values 3388 if (!$this->empty_string($fontname)) { 3389 $prev_FontFamily = $this->FontFamily; 3390 $prev_FontStyle = $this->FontStyle; 3391 $prev_FontSizePt = $this->FontSizePt; 3392 $this->SetFont($fontname, $fontstyle, $fontsize); 3393 } 3394 // convert UTF-8 array to Latin1 if required 3395 $sa = $this->UTF8ArrToLatin1($sa); 3396 $w = 0; // total width 3397 $wa = array(); // array of characters widths 3398 foreach ($sa as $char) { 3399 // character width 3400 $cw = $this->GetCharWidth($char); 3401 $wa[] = $cw; 3402 $w += $cw; 3403 } 3404 // restore previous values 3405 if (!$this->empty_string($fontname)) { 3406 $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt); 3407 } 3408 if ($getarray) { 3409 return $wa; 3410 } 3411 return $w; 3412 } 3413 3414 /** 3415 * Returns the length of the char in user unit for the current font. 3416 * @param int $char The char code whose length is to be returned 3417 * @return int char width 3418 * @author Nicola Asuni 3419 * @access public 3420 * @since 2.4.000 (2008-03-06) 3421 */ 3422 public function GetCharWidth($char) { 3423 if ($char == 173) { 3424 // SHY character will not be printed 3425 return (0); 3426 } 3427 $cw = &$this->CurrentFont['cw']; 3428 if (isset($cw[$char])) { 3429 $w = $cw[$char]; 3430 } elseif (isset($this->CurrentFont['dw'])) { 3431 // default width 3432 $w = $this->CurrentFont['dw']; 3433 } elseif (isset($cw[32])) { 3434 // default width 3435 $w = $cw[32]; 3436 } else { 3437 $w = 600; 3438 } 3439 return ($w * $this->FontSize / 1000); 3440 } 3441 3442 /** 3443 * Returns the numbero of characters in a string. 3444 * @param string $s The input string. 3445 * @return int number of characters 3446 * @access public 3447 * @since 2.0.0001 (2008-01-07) 3448 */ 3449 public function GetNumChars($s) { 3450 if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) { 3451 return count($this->UTF8StringToArray($s)); 3452 } 3453 return strlen($s); 3454 } 3455 3456 /** 3457 * Fill the list of available fonts ($this->fontlist). 3458 * @access protected 3459 * @since 4.0.013 (2008-07-28) 3460 */ 3461 protected function getFontsList() { 3462 $fontsdir = opendir($this->_getfontpath()); 3463 while (($file = readdir($fontsdir)) !== false) { 3464 if (substr($file, -4) == '.php') { 3465 array_push($this->fontlist, strtolower(basename($file, '.php'))); 3466 } 3467 } 3468 closedir($fontsdir); 3469 } 3470 3471 /** 3472 * Imports a TrueType, Type1, core, or CID0 font and makes it available. 3473 * It is necessary to generate a font definition file first (read /fonts/utils/README.TXT). 3474 * The definition file (and the font file itself when embedding) must be present either in the current directory or in the one indicated by K_PATH_FONTS if the constant is defined. If it could not be found, the error "Could not include font definition file" is generated. 3475 * @param string $family Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font. 3476 * @param string $style Font style. Possible values are (case insensitive):<ul><li>empty string: regular (default)</li><li>B: bold</li><li>I: italic</li><li>BI or IB: bold italic</li></ul> 3477 * @param string $fontfile The font definition file. By default, the name is built from the family and style, in lower case with no spaces. 3478 * @return array containing the font data, or false in case of error. 3479 * @access public 3480 * @since 1.5 3481 * @see SetFont() 3482 */ 3483 public function AddFont($family, $style='', $fontfile='') { 3484 if ($this->empty_string($family)) { 3485 if (!$this->empty_string($this->FontFamily)) { 3486 $family = $this->FontFamily; 3487 } else { 3488 $this->Error('Empty font family'); 3489 } 3490 } 3491 $family = strtolower($family); 3492 if ((!$this->isunicode) AND ($family == 'arial')) { 3493 $family = 'helvetica'; 3494 } 3495 if (($family == 'symbol') OR ($family == 'zapfdingbats')) { 3496 $style = ''; 3497 } 3498 $tempstyle = strtoupper($style); 3499 $style = ''; 3500 // underline 3501 if (strpos($tempstyle, 'U') !== false) { 3502 $this->underline = true; 3503 } else { 3504 $this->underline = false; 3505 } 3506 // line-through (deleted) 3507 if (strpos($tempstyle, 'D') !== false) { 3508 $this->linethrough = true; 3509 } else { 3510 $this->linethrough = false; 3511 } 3512 // overline 3513 if (strpos($tempstyle, 'O') !== false) { 3514 $this->overline = true; 3515 } else { 3516 $this->overline = false; 3517 } 3518 // bold 3519 if (strpos($tempstyle, 'B') !== false) { 3520 $style .= 'B'; 3521 } 3522 // oblique 3523 if (strpos($tempstyle, 'I') !== false) { 3524 $style .= 'I'; 3525 } 3526 $bistyle = $style; 3527 $fontkey = $family.$style; 3528 $font_style = $style.($this->underline ? 'U' : '').($this->linethrough ? 'D' : '').($this->overline ? 'O' : ''); 3529 $fontdata = array('fontkey' => $fontkey, 'family' => $family, 'style' => $font_style); 3530 // check if the font has been already added 3531 if ($this->getFontBuffer($fontkey) !== false) { 3532 return $fontdata; 3533 } 3534 if (isset($type)) { 3535 unset($type); 3536 } 3537 if (isset($cw)) { 3538 unset($cw); 3539 } 3540 // get specified font directory (if any) 3541 $fontdir = false; 3542 if (!$this->empty_string($fontfile)) { 3543 $fontdir = dirname($fontfile); 3544 if ($this->empty_string($fontdir) OR ($fontdir == '.')) { 3545 $fontdir = ''; 3546 } else { 3547 $fontdir .= '/'; 3548 } 3549 } 3550 // search and include font file 3551 if ($this->empty_string($fontfile) OR (!file_exists($fontfile))) { 3552 // build a standard filenames for specified font 3553 $fontfile1 = str_replace(' ', '', $family).strtolower($style).'.php'; 3554 $fontfile2 = str_replace(' ', '', $family).'.php'; 3555 // search files on various directories 3556 if (($fontdir !== false) AND file_exists($fontdir.$fontfile1)) { 3557 $fontfile = $fontdir.$fontfile1; 3558 } elseif (file_exists($this->_getfontpath().$fontfile1)) { 3559 $fontfile = $this->_getfontpath().$fontfile1; 3560 } elseif (file_exists($fontfile1)) { 3561 $fontfile = $fontfile1; 3562 } elseif (($fontdir !== false) AND file_exists($fontdir.$fontfile2)) { 3563 $fontfile = $fontdir.$fontfile2; 3564 } elseif (file_exists($this->_getfontpath().$fontfile2)) { 3565 $fontfile = $this->_getfontpath().$fontfile2; 3566 } else { 3567 $fontfile = $fontfile2; 3568 } 3569 } 3570 // include font file 3571 if (file_exists($fontfile)) { 3572 include($fontfile); 3573 } else { 3574 $this->Error('Could not include font definition file: '.$family.''); 3575 } 3576 // check font parameters 3577 if ((!isset($type)) OR (!isset($cw))) { 3578 $this->Error('The font definition file has a bad format: '.$fontfile.''); 3579 } 3580 // SET default parameters 3581 if (!isset($file) OR $this->empty_string($file)) { 3582 $file = ''; 3583 } 3584 if (!isset($enc) OR $this->empty_string($enc)) { 3585 $enc = ''; 3586 } 3587 if (!isset($cidinfo) OR $this->empty_string($cidinfo)) { 3588 $cidinfo = array('Registry'=>'Adobe','Ordering'=>'Identity','Supplement'=>0); 3589 $cidinfo['uni2cid'] = array(); 3590 } 3591 if (!isset($ctg) OR $this->empty_string($ctg)) { 3592 $ctg = ''; 3593 } 3594 if (!isset($desc) OR $this->empty_string($desc)) { 3595 $desc = array(); 3596 } 3597 if (!isset($up) OR $this->empty_string($up)) { 3598 $up = -100; 3599 } 3600 if (!isset($ut) OR $this->empty_string($ut)) { 3601 $ut = 50; 3602 } 3603 if (!isset($cw) OR $this->empty_string($cw)) { 3604 $cw = array(); 3605 } 3606 if (!isset($dw) OR $this->empty_string($dw)) { 3607 // set default width 3608 if (isset($desc['MissingWidth']) AND ($desc['MissingWidth'] > 0)) { 3609 $dw = $desc['MissingWidth']; 3610 } elseif (isset($cw[32])) { 3611 $dw = $cw[32]; 3612 } else { 3613 $dw = 600; 3614 } 3615 } 3616 ++$this->numfonts; 3617 if ($type == 'cidfont0') { 3618 // register CID font (all styles at once) 3619 $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic'); 3620 $sname = $name.$styles[$bistyle]; 3621 // artificial bold 3622 if (strpos($bistyle, 'B') !== false) { 3623 if (isset($desc['StemV'])) { 3624 $desc['StemV'] *= 2; 3625 } else { 3626 $desc['StemV'] = 120; 3627 } 3628 } 3629 // artificial italic 3630 if (strpos($bistyle, 'I') !== false) { 3631 if (isset($desc['ItalicAngle'])) { 3632 $desc['ItalicAngle'] -= 11; 3633 } else { 3634 $desc['ItalicAngle'] = -11; 3635 } 3636 } 3637 } elseif ($type == 'core') { 3638 $name = $this->CoreFonts[$fontkey]; 3639 } elseif (($type == 'TrueType') OR ($type == 'Type1')) { 3640 // ... 3641 } elseif ($type == 'TrueTypeUnicode') { 3642 $enc = 'Identity-H'; 3643 } else { 3644 $this->Error('Unknow font type: '.$type.''); 3645 } 3646 $this->setFontBuffer($fontkey, array('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)); 3647 if (isset($diff) AND (!empty($diff))) { 3648 //Search existing encodings 3649 $d = 0; 3650 $nb = count($this->diffs); 3651 for ($i=1; $i <= $nb; ++$i) { 3652 if ($this->diffs[$i] == $diff) { 3653 $d = $i; 3654 break; 3655 } 3656 } 3657 if ($d == 0) { 3658 $d = $nb + 1; 3659 $this->diffs[$d] = $diff; 3660 } 3661 $this->setFontSubBuffer($fontkey, 'diff', $d); 3662 } 3663 if (!$this->empty_string($file)) { 3664 if ((strcasecmp($type,'TrueType') == 0) OR (strcasecmp($type, 'TrueTypeUnicode') == 0)) { 3665 $this->FontFiles[$file] = array('length1' => $originalsize, 'fontdir' => $fontdir); 3666 } elseif ($type != 'core') { 3667 $this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2, 'fontdir' => $fontdir); 3668 } 3669 } 3670 return $fontdata; 3671 } 3672 3673 /** 3674 * Sets the font used to print character strings. 3675 * The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe). 3676 * The method can be called before the first page is created and the font is retained from page to page. 3677 * If you just wish to change the current font size, it is simpler to call SetFontSize(). 3678 * Note: for the standard fonts, the font metric files must be accessible. There are three possibilities for this:<ul><li>They are in the current directory (the one where the running script lies)</li><li>They are in one of the directories defined by the include_path parameter</li><li>They are in the directory defined by the K_PATH_FONTS constant</li></ul><br /> 3679 * @param string $family Family font. It can be either a name defined by AddFont() or one of the standard Type1 families (case insensitive):<ul><li>times (Times-Roman)</li><li>timesb (Times-Bold)</li><li>timesi (Times-Italic)</li><li>timesbi (Times-BoldItalic)</li><li>helvetica (Helvetica)</li><li>helveticab (Helvetica-Bold)</li><li>helveticai (Helvetica-Oblique)</li><li>helveticabi (Helvetica-BoldOblique)</li><li>courier (Courier)</li><li>courierb (Courier-Bold)</li><li>courieri (Courier-Oblique)</li><li>courierbi (Courier-BoldOblique)</li><li>symbol (Symbol)</li><li>zapfdingbats (ZapfDingbats)</li></ul> It is also possible to pass an empty string. In that case, the current family is retained. 3680 * @param string $style Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line trough</li><li>O: overline</li></ul> or any combination. The default value is regular. Bold and italic styles do not apply to Symbol and ZapfDingbats basic fonts or other fonts when not defined. 3681 * @param float $size Font size in points. The default value is the current size. If no size has been specified since the beginning of the document, the value taken is 12 3682 * @param string $fontfile The font definition file. By default, the name is built from the family and style, in lower case with no spaces. 3683 * @access public 3684 * @since 1.0 3685 * @see AddFont(), SetFontSize() 3686 */ 3687 public function SetFont($family, $style='', $size=0, $fontfile='') { 3688 //Select a font; size given in points 3689 if ($size == 0) { 3690 $size = $this->FontSizePt; 3691 } 3692 // try to add font (if not already added) 3693 $fontdata = $this->AddFont($family, $style, $fontfile); 3694 $this->FontFamily = $fontdata['family']; 3695 $this->FontStyle = $fontdata['style']; 3696 $this->CurrentFont = $this->getFontBuffer($fontdata['fontkey']); 3697 $this->SetFontSize($size); 3698 } 3699 3700 /** 3701 * Defines the size of the current font. 3702 * @param float $size The size (in points) 3703 * @access public 3704 * @since 1.0 3705 * @see SetFont() 3706 */ 3707 public function SetFontSize($size) { 3708 //Set font size in points 3709 $this->FontSizePt = $size; 3710 $this->FontSize = $size / $this->k; 3711 if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) { 3712 $this->FontAscent = $this->CurrentFont['desc']['Ascent'] * $this->FontSize / 1000; 3713 } else { 3714 $this->FontAscent = 0.85 * $this->FontSize; 3715 } 3716 if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] <= 0)) { 3717 $this->FontDescent = - $this->CurrentFont['desc']['Descent'] * $this->FontSize / 1000; 3718 } else { 3719 $this->FontDescent = 0.15 * $this->FontSize; 3720 } 3721 if (($this->page > 0) AND (isset($this->CurrentFont['i']))) { 3722 $this->_out(sprintf('BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt)); 3723 } 3724 } 3725 3726 /** 3727 * Return the font descent value 3728 * @param string $font font name 3729 * @param string $style font style 3730 * @param float $size The size (in points) 3731 * @return int font descent 3732 * @access public 3733 * @since 4.9.003 (2010-03-30) 3734 */ 3735 public function getFontDescent($font, $style='', $size=0) { 3736 //Set font size in points 3737 $sizek = $size / $this->k; 3738 $fontdata = $this->AddFont($font, $style); 3739 if (isset($fontdata['desc']['Descent']) AND ($fontdata['desc']['Descent'] <= 0)) { 3740 $descent = - $fontdata['desc']['Descent'] * $sizek / 1000; 3741 } else { 3742 $descent = 0.15 * $sizek; 3743 } 3744 return $descent; 3745 } 3746 3747 /** 3748 * Return the font ascent value 3749 * @param string $font font name 3750 * @param string $style font style 3751 * @param float $size The size (in points) 3752 * @return int font ascent 3753 * @access public 3754 * @since 4.9.003 (2010-03-30) 3755 */ 3756 public function getFontAscent($font, $style='', $size=0) { 3757 //Set font size in points 3758 $sizek = $size / $this->k; 3759 $fontdata = $this->AddFont($font, $style); 3760 if (isset($fontdata['desc']['Ascent']) AND ($fontdata['desc']['Ascent'] > 0)) { 3761 $ascent = $fontdata['desc']['Ascent'] * $sizek / 1000; 3762 } else { 3763 $ascent = 0.85 * $sizek; 3764 } 3765 return $ascent; 3766 } 3767 3768 /** 3769 * Defines the default monospaced font. 3770 * @param string $font Font name. 3771 * @access public 3772 * @since 4.5.025 3773 */ 3774 public function SetDefaultMonospacedFont($font) { 3775 $this->default_monospaced_font = $font; 3776 } 3777 3778 /** 3779 * Creates a new internal link and returns its identifier. An internal link is a clickable area which directs to another place within the document.<br /> 3780 * The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink(). 3781 * @access public 3782 * @since 1.5 3783 * @see Cell(), Write(), Image(), Link(), SetLink() 3784 */ 3785 public function AddLink() { 3786 //Create a new internal link 3787 $n = count($this->links) + 1; 3788 $this->links[$n] = array(0, 0); 3789 return $n; 3790 } 3791 3792 /** 3793 * Defines the page and position a link points to. 3794 * @param int $link The link identifier returned by AddLink() 3795 * @param float $y Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page) 3796 * @param int $page Number of target page; -1 indicates the current page. This is the default value 3797 * @access public 3798 * @since 1.5 3799 * @see AddLink() 3800 */ 3801 public function SetLink($link, $y=0, $page=-1) { 3802 if ($y == -1) { 3803 $y = $this->y; 3804 } 3805 if ($page == -1) { 3806 $page = $this->page; 3807 } 3808 $this->links[$link] = array($page, $y); 3809 } 3810 3811 /** 3812 * Puts a link on a rectangular area of the page. 3813 * Text or image links are generally put via Cell(), Write() or Image(), but this method can be useful for instance to define a clickable area inside an image. 3814 * @param float $x Abscissa of the upper-left corner of the rectangle 3815 * @param float $y Ordinate of the upper-left corner of the rectangle 3816 * @param float $w Width of the rectangle 3817 * @param float $h Height of the rectangle 3818 * @param mixed $link URL or identifier returned by AddLink() 3819 * @param int $spaces number of spaces on the text to link 3820 * @access public 3821 * @since 1.5 3822 * @see AddLink(), Annotation(), Cell(), Write(), Image() 3823 */ 3824 public function Link($x, $y, $w, $h, $link, $spaces=0) { 3825 $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'), $spaces); 3826 } 3827 3828 /** 3829 * Puts a markup annotation on a rectangular area of the page. 3830 * !!!!THE ANNOTATION SUPPORT IS NOT YET FULLY IMPLEMENTED !!!! 3831 * @param float $x Abscissa of the upper-left corner of the rectangle 3832 * @param float $y Ordinate of the upper-left corner of the rectangle 3833 * @param float $w Width of the rectangle 3834 * @param float $h Height of the rectangle 3835 * @param string $text annotation text or alternate content 3836 * @param array $opt array of options (see section 8.4 of PDF reference 1.7). 3837 * @param int $spaces number of spaces on the text to link 3838 * @access public 3839 * @since 4.0.018 (2008-08-06) 3840 */ 3841 public function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text'), $spaces=0) { 3842 if ($x === '') { 3843 $x = $this->x; 3844 } 3845 if ($y === '') { 3846 $y = $this->y; 3847 } 3848 // recalculate coordinates to account for graphic transformations 3849 if (isset($this->transfmatrix)) { 3850 for ($i=$this->transfmatrix_key; $i > 0; --$i) { 3851 $maxid = count($this->transfmatrix[$i]) - 1; 3852 for ($j=$maxid; $j >= 0; --$j) { 3853 $ctm = $this->transfmatrix[$i][$j]; 3854 if (isset($ctm['a'])) { 3855 $x = $x * $this->k; 3856 $y = ($this->h - $y) * $this->k; 3857 $w = $w * $this->k; 3858 $h = $h * $this->k; 3859 // top left 3860 $xt = $x; 3861 $yt = $y; 3862 $x1 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e']; 3863 $y1 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f']; 3864 // top right 3865 $xt = $x + $w; 3866 $yt = $y; 3867 $x2 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e']; 3868 $y2 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f']; 3869 // bottom left 3870 $xt = $x; 3871 $yt = $y - $h; 3872 $x3 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e']; 3873 $y3 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f']; 3874 // bottom right 3875 $xt = $x + $w; 3876 $yt = $y - $h; 3877 $x4 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e']; 3878 $y4 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f']; 3879 // new coordinates (rectangle area) 3880 $x = min($x1, $x2, $x3, $x4); 3881 $y = max($y1, $y2, $y3, $y4); 3882 $w = (max($x1, $x2, $x3, $x4) - $x) / $this->k; 3883 $h = ($y - min($y1, $y2, $y3, $y4)) / $this->k; 3884 $x = $x / $this->k; 3885 $y = $this->h - ($y / $this->k); 3886 } 3887 } 3888 } 3889 } 3890 if ($this->page <= 0) { 3891 $page = 1; 3892 } else { 3893 $page = $this->page; 3894 } 3895 if (!isset($this->PageAnnots[$page])) { 3896 $this->PageAnnots[$page] = array(); 3897 } 3898 $this->PageAnnots[$page][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces); 3899 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'])]))) { 3900 $this->embeddedfiles[basename($opt['FS'])] = array('file' => $opt['FS'], 'n' => (count($this->embeddedfiles) + $this->embedded_start_obj_id)); 3901 } 3902 // Add widgets annotation's icons 3903 if (isset($opt['mk']['i']) AND file_exists($opt['mk']['i'])) { 3904 $this->Image($opt['mk']['i'], '', '', 10, 10, '', '', '', false, 300, '', false, false, 0, false, true); 3905 } 3906 if (isset($opt['mk']['ri']) AND file_exists($opt['mk']['ri'])) { 3907 $this->Image($opt['mk']['ri'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true); 3908 } 3909 if (isset($opt['mk']['ix']) AND file_exists($opt['mk']['ix'])) { 3910 $this->Image($opt['mk']['ix'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true); 3911 } 3912 ++$this->annot_obj_id; 3913 } 3914 3915 /** 3916 * Embedd the attached files. 3917 * @since 4.4.000 (2008-12-07) 3918 * @access protected 3919 * @see Annotation() 3920 */ 3921 protected function _putEmbeddedFiles() { 3922 reset($this->embeddedfiles); 3923 foreach ($this->embeddedfiles as $filename => $filedata) { 3924 $data = file_get_contents($filedata['file']); 3925 $filter = ''; 3926 if ($this->compress) { 3927 $data = gzcompress($data); 3928 $filter = ' /Filter /FlateDecode'; 3929 } 3930 $this->offsets[$filedata['n']] = $this->bufferlen; 3931 $out = $filedata['n'].' 0 obj'; 3932 $out .= ' <</Type /EmbeddedFile'.$filter.' /Length '.strlen($data).' >>'; 3933 $out .= ' '.$this->_getstream($data, $filedata['n']); 3934 $out .= ' endobj'; 3935 $this->_out($out); 3936 } 3937 } 3938 3939 /** 3940 * Prints a text cell at the specified position. 3941 * The origin is on the left of the first charcter, on the baseline. 3942 * This method allows to place a string precisely on the page. 3943 * @param float $x Abscissa of the cell origin 3944 * @param float $y Ordinate of the cell origin 3945 * @param string $txt String to print 3946 * @param int $fstroke outline size in user units (false = disable) 3947 * @param boolean $fclip if true activate clipping mode (you must call StartTransform() before this function and StopTransform() to stop the clipping tranformation). 3948 * @param boolean $ffill if true fills the text 3949 * @param mixed $border Indicates if borders must be drawn around the cell. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> 3950 * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL languages)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul>Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0. 3951 * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul> 3952 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. 3953 * @param mixed $link URL or identifier returned by AddLink(). 3954 * @param int $stretch stretch carachter mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if necessary</li><li>2 = forced horizontal scaling</li><li>3 = character spacing only if necessary</li><li>4 = forced character spacing</li></ul> 3955 * @param boolean $ignore_min_height if true ignore automatic minimum height value. 3956 * @param string $calign cell vertical alignment relative to the specified Y value. Possible values are:<ul><li>T : cell top</li><li>A : font top</li><li>L : font baseline</li><li>D : font bottom</li><li>B : cell bottom</li></ul> 3957 * @param string $valign text vertical alignment inside the cell. Possible values are:<ul><li>T : top</li><li>C : center</li><li>B : bottom</li></ul> 3958 * @param boolean $rtloff if true uses the page top-left corner as origin of axis for $x and $y initial position. 3959 * @access public 3960 * @since 1.0 3961 * @see Cell(), Write(), MultiCell(), WriteHTML(), WriteHTMLCell() 3962 */ 3963 public function Text($x, $y, $txt, $fstroke=false, $fclip=false, $ffill=true, $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M', $rtloff=false) { 3964 $textrendermode = $this->textrendermode; 3965 $textstrokewidth = $this->textstrokewidth; 3966 $this->setTextRenderingMode($fstroke, $ffill, $fclip); 3967 $this->SetXY($x, $y, $rtloff); 3968 $this->Cell(0, 0, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height, $calign, $valign); 3969 // restore previous rendering mode 3970 $this->textrendermode = $textrendermode; 3971 $this->textstrokewidth = $textstrokewidth; 3972 } 3973 3974 /** 3975 * Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value. 3976 * The default implementation returns a value according to the mode selected by SetAutoPageBreak().<br /> 3977 * This method is called automatically and should not be called directly by the application. 3978 * @return boolean 3979 * @access public 3980 * @since 1.4 3981 * @see SetAutoPageBreak() 3982 */ 3983 public function AcceptPageBreak() { 3984 if ($this->num_columns > 1) { 3985 // multi column mode 3986 if($this->current_column < ($this->num_columns - 1)) { 3987 // go to next column 3988 $this->selectColumn($this->current_column + 1); 3989 } else { 3990 // add a new page 3991 $this->AddPage(); 3992 // set first column 3993 $this->selectColumn(0); 3994 } 3995 // avoid page breaking from checkPageBreak() 3996 return false; 3997 } 3998 return $this->AutoPageBreak; 3999 } 4000 4001 /** 4002 * Add page if needed. 4003 * @param float $h Cell height. Default value: 0. 4004 * @param mixed $y starting y position, leave empty for current position. 4005 * @param boolean $addpage if true add a page, otherwise only return the true/false state 4006 * @return boolean true in case of page break, false otherwise. 4007 * @since 3.2.000 (2008-07-01) 4008 * @access protected 4009 */ 4010 protected function checkPageBreak($h=0, $y='', $addpage=true) { 4011 if ($this->empty_string($y)) { 4012 $y = $this->y; 4013 } 4014 if ((($y + $h) > $this->PageBreakTrigger) AND (!$this->InFooter) AND ($this->AcceptPageBreak())) { 4015 if ($addpage) { 4016 //Automatic page break 4017 $x = $this->x; 4018 $this->AddPage($this->CurOrientation); 4019 $this->y = $this->tMargin; 4020 $oldpage = $this->page - 1; 4021 if ($this->rtl) { 4022 if ($this->pagedim[$this->page]['orm'] != $this->pagedim[$oldpage]['orm']) { 4023 $this->x = $x - ($this->pagedim[$this->page]['orm'] - $this->pagedim[$oldpage]['orm']); 4024 } else { 4025 $this->x = $x; 4026 } 4027 } else { 4028 if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) { 4029 $this->x = $x + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$oldpage]['olm']); 4030 } else { 4031 $this->x = $x; 4032 } 4033 } 4034 } 4035 return true; 4036 } 4037 return false; 4038 } 4039 4040 /** 4041 * Prints a cell (rectangular area) with optional borders, background color and character string. The upper-left corner of the cell corresponds to the current position. The text can be aligned or centered. After the call, the current position moves to the right or to the next line. It is possible to put a link on the text.<br /> 4042 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting. 4043 * @param float $w Cell width. If 0, the cell extends up to the right margin. 4044 * @param float $h Cell height. Default value: 0. 4045 * @param string $txt String to print. Default value: empty string. 4046 * @param mixed $border Indicates if borders must be drawn around the cell. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> 4047 * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL languages)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul> 4048 Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0. 4049 * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul> 4050 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. 4051 * @param mixed $link URL or identifier returned by AddLink(). 4052 * @param int $stretch stretch carachter mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if necessary</li><li>2 = forced horizontal scaling</li><li>3 = character spacing only if necessary</li><li>4 = forced character spacing</li></ul> 4053 * @param boolean $ignore_min_height if true ignore automatic minimum height value. 4054 * @param string $calign cell vertical alignment relative to the specified Y value. Possible values are:<ul><li>T : cell top</li><li>C : center</li><li>B : cell bottom</li><li>A : font top</li><li>L : font baseline</li><li>D : font bottom</li></ul> 4055 * @param string $valign text vertical alignment inside the cell. Possible values are:<ul><li>T : top</li><li>C : center</li><li>B : bottom</li></ul> 4056 * @access public 4057 * @since 1.0 4058 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak() 4059 */ 4060 public function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M') { 4061 if (!$ignore_min_height) { 4062 $min_cell_height = $this->FontSize * $this->cell_height_ratio; 4063 if ($h < $min_cell_height) { 4064 $h = $min_cell_height; 4065 } 4066 } 4067 $this->checkPageBreak($h); 4068 $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height, $calign, $valign)); 4069 } 4070 4071 /** 4072 * Removes SHY characters from text. 4073 * @param string $txt input string 4074 * @return string without SHY characters. 4075 * @access public 4076 * @since (4.5.019) 2009-02-28 4077 */ 4078 public function removeSHY($txt='') { 4079 /* 4080 * Unicode Data 4081 * Name : SOFT HYPHEN, commonly abbreviated as SHY 4082 * HTML Entity (decimal): ­ 4083 * HTML Entity (hex): ­ 4084 * HTML Entity (named): ­ 4085 * How to type in Microsoft Windows: [Alt +00AD] or [Alt 0173] 4086 * UTF-8 (hex): 0xC2 0xAD (c2ad) 4087 * UTF-8 character: chr(194).chr(173) 4088 */ 4089 $txt = preg_replace('/([\\xc2]{1}[\\xad]{1})/', '', $txt); 4090 if (!$this->isunicode) { 4091 $txt = preg_replace('/([\\xad]{1})/', '', $txt); 4092 } 4093 return $txt; 4094 } 4095 4096 /** 4097 * Returns the PDF string code to print a cell (rectangular area) with optional borders, background color and character string. The upper-left corner of the cell corresponds to the current position. The text can be aligned or centered. After the call, the current position moves to the right or to the next line. It is possible to put a link on the text.<br /> 4098 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting. 4099 * @param float $w Cell width. If 0, the cell extends up to the right margin. 4100 * @param float $h Cell height. Default value: 0. 4101 * @param string $txt String to print. Default value: empty string. 4102 * @param mixed $border Indicates if borders must be drawn around the cell. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> 4103 * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL languages)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul>Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0. 4104 * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul> 4105 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. 4106 * @param mixed $link URL or identifier returned by AddLink(). 4107 * @param int $stretch stretch carachter mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if necessary</li><li>2 = forced horizontal scaling</li><li>3 = character spacing only if necessary</li><li>4 = forced character spacing</li></ul> 4108 * @param boolean $ignore_min_height if true ignore automatic minimum height value. 4109 * @param string $calign cell vertical alignment relative to the specified Y value. Possible values are:<ul><li>T : cell top</li><li>C : center</li><li>B : cell bottom</li><li>A : font top</li><li>L : font baseline</li><li>D : font bottom</li></ul> 4110 * @param string $valign text vertical alignment inside the cell. Possible values are:<ul><li>T : top</li><li>C : center</li><li>B : bottom</li></ul> 4111 * @access protected 4112 * @since 1.0 4113 * @see Cell() 4114 */ 4115 protected function getCellCode($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M') { 4116 $txt = $this->removeSHY($txt); 4117 $rs = ''; //string to be returned 4118 if (!$ignore_min_height) { 4119 $min_cell_height = $this->FontSize * $this->cell_height_ratio; 4120 if ($h < $min_cell_height) { 4121 $h = $min_cell_height; 4122 } 4123 } 4124 $k = $this->k; 4125 $x = $this->x; 4126 $y = $this->y; 4127 // cell vertical alignment 4128 switch ($calign) { 4129 case 'A': { 4130 // font top 4131 switch ($valign) { 4132 case 'T': { 4133 // top 4134 $y -= ($this->LineWidth / 2); 4135 break; 4136 } 4137 case 'B': { 4138 // bottom 4139 $y -= ($h - $this->FontAscent - $this->FontDescent - ($this->LineWidth / 2)); 4140 break; 4141 } 4142 default: 4143 case 'M': { 4144 // center 4145 $y -= (($h - $this->FontAscent - $this->FontDescent) / 2); 4146 break; 4147 } 4148 } 4149 break; 4150 } 4151 case 'L': { 4152 // font baseline 4153 switch ($valign) { 4154 case 'T': { 4155 // top 4156 $y -= ($this->FontAscent + ($this->LineWidth / 2)); 4157 break; 4158 } 4159 case 'B': { 4160 // bottom 4161 $y -= ($h - $this->FontDescent - ($this->LineWidth / 2)); 4162 break; 4163 } 4164 default: 4165 case 'M': { 4166 // center 4167 $y -= (($h + $this->FontAscent - $this->FontDescent) / 2); 4168 break; 4169 } 4170 } 4171 break; 4172 } 4173 case 'D': { 4174 // font bottom 4175 switch ($valign) { 4176 case 'T': { 4177 // top 4178 $y -= ($this->FontAscent + $this->FontDescent + ($this->LineWidth / 2)); 4179 break; 4180 } 4181 case 'B': { 4182 // bottom 4183 $y -= ($h - ($this->LineWidth / 2)); 4184 break; 4185 } 4186 default: 4187 case 'M': { 4188 // center 4189 $y -= (($h + $this->FontAscent + $this->FontDescent) / 2); 4190 break; 4191 } 4192 } 4193 break; 4194 } 4195 case 'B': { 4196 // cell bottom 4197 $y -= $h; 4198 break; 4199 } 4200 case 'C': { 4201 // cell center 4202 $y -= ($h / 2); 4203 break; 4204 } 4205 default: 4206 case 'T': { 4207 // cell top 4208 break; 4209 } 4210 } 4211 // text vertical alignment 4212 switch ($valign) { 4213 case 'T': { 4214 // top 4215 $basefonty = $y + $this->FontAscent + ($this->LineWidth / 2); 4216 break; 4217 } 4218 case 'B': { 4219 // bottom 4220 $basefonty = $y + $h - $this->FontDescent - ($this->LineWidth / 2); 4221 break; 4222 } 4223 default: 4224 case 'M': { 4225 // center 4226 $basefonty = $y + (($h + $this->FontAscent - $this->FontDescent) / 2); 4227 break; 4228 } 4229 } 4230 if ($this->empty_string($w) OR ($w <= 0)) { 4231 if ($this->rtl) { 4232 $w = $x - $this->lMargin; 4233 } else { 4234 $w = $this->w - $this->rMargin - $x; 4235 } 4236 } 4237 $s = ''; 4238 // fill and borders 4239 if (($fill == 1) OR ($border == 1)) { 4240 if ($fill == 1) { 4241 $op = ($border == 1) ? 'B' : 'f'; 4242 } else { 4243 $op = 'S'; 4244 } 4245 if ($this->rtl) { 4246 $xk = (($this->x - $w) * $k); 4247 } else { 4248 $xk = ($this->x * $k); 4249 } 4250 $s .= sprintf('%.2F %.2F %.2F %.2F re %s ', $xk, (($this->h - $y) * $k), ($w * $k), (-$h * $k), $op); 4251 } 4252 if (is_string($border)) { 4253 $lm = ($this->LineWidth / 2); 4254 if (strpos($border,'L') !== false) { 4255 if ($this->rtl) { 4256 $xk = ($x - $w) * $k; 4257 } else { 4258 $xk = $x * $k; 4259 } 4260 $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - $y + $lm) * $k), $xk, (($this->h - ($y + $h + $lm)) * $k)); 4261 } 4262 if (strpos($border,'T') !== false) { 4263 if ($this->rtl) { 4264 $xk = ($x - $w + $lm) * $k; 4265 $xwk = ($x - $lm) * $k; 4266 } else { 4267 $xk = ($x - $lm) * $k; 4268 $xwk = ($x + $w + $lm) * $k; 4269 } 4270 $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - $y) * $k), $xwk, (($this->h - $y) * $k)); 4271 } 4272 if (strpos($border,'R') !== false) { 4273 if ($this->rtl) { 4274 $xk = $x * $k; 4275 } else { 4276 $xk = ($x + $w) * $k; 4277 } 4278 $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - $y + $lm) * $k), $xk, (($this->h - ($y + $h + $lm))* $k)); 4279 } 4280 if (strpos($border,'B') !== false) { 4281 if ($this->rtl) { 4282 $xk = ($x - $w + $lm) * $k; 4283 $xwk = ($x - $lm) * $k; 4284 } else { 4285 $xk = ($x - $lm) * $k; 4286 $xwk = ($x + $w + $lm) * $k; 4287 } 4288 $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - ($y + $h)) * $k), $xwk, (($this->h - ($y + $h)) * $k)); 4289 } 4290 } 4291 if ($txt != '') { 4292 $txt2 = $txt; 4293 if ($this->isunicode) { 4294 if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) { 4295 $txt2 = $this->UTF8ToLatin1($txt2); 4296 } else { 4297 $unicode = $this->UTF8StringToArray($txt); // array of UTF-8 unicode values 4298 $unicode = $this->utf8Bidi($unicode, '', $this->tmprtl); 4299 if (defined('K_THAI_TOPCHARS') AND (K_THAI_TOPCHARS == true)) { 4300 // ---- Fix for bug #2977340 "Incorrect Thai characters position arrangement" ---- 4301 // NOTE: this doesn't work with HTML justification 4302 // Symbols that could overlap on the font top (only works in LTR) 4303 $topchar = array(3611, 3613, 3615, 3650, 3651, 3652); // chars that extends on top 4304 $topsym = array(3633, 3636, 3637, 3638, 3639, 3655, 3656, 3657, 3658, 3659, 3660, 3661, 3662); // symbols with top position 4305 $numchars = count($unicode); // number of chars 4306 $unik = 0; 4307 $uniblock = array(); 4308 $uniblock[$unik] = array(); 4309 $uniblock[$unik][] = $unicode[0]; 4310 // resolve overlapping conflicts by splitting the string in several parts 4311 for ($i = 1; $i < $numchars; ++$i) { 4312 // check if symbols overlaps at top 4313 if (in_array($unicode[$i], $topsym) AND (in_array($unicode[($i - 1)], $topsym) OR in_array($unicode[($i - 1)], $topchar))) { 4314 // move symbols to another array 4315 ++$unik; 4316 $uniblock[$unik] = array(); 4317 $uniblock[$unik][] = $unicode[$i]; 4318 ++$unik; 4319 $uniblock[$unik] = array(); 4320 $unicode[$i] = 8203; // Unicode Character 'ZERO WIDTH SPACE' (U+200B) 4321 } else { 4322 $uniblock[$unik][] = $unicode[$i]; 4323 } 4324 } 4325 // ---- END OF Fix for bug #2977340 4326 } 4327 $txt2 = $this->arrUTF8ToUTF16BE($unicode, false); 4328 } 4329 } 4330 $txt2 = $this->_escape($txt2); 4331 // text length 4332 $txwidth = $this->GetStringWidth($txt); 4333 $width = $txwidth; 4334 // ratio between cell length and text length 4335 if ($width <= 0) { 4336 $ratio = 1; 4337 } else { 4338 $ratio = ($w - (2 * $this->cMargin)) / $width; 4339 } 4340 // stretch text if required 4341 if (($stretch > 0) AND (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0)))) { 4342 if ($stretch > 2) { 4343 // spacing 4344 //Calculate character spacing in points 4345 $char_space = (($w - $width - (2 * $this->cMargin)) * $this->k) / max($this->GetNumChars($txt)-1,1); 4346 //Set character spacing 4347 $rs .= sprintf('BT %.2F Tc ET ', $char_space); 4348 } else { 4349 // scaling 4350 //Calculate horizontal scaling 4351 $horiz_scale = $ratio * 100.0; 4352 //Set horizontal scaling 4353 $rs .= sprintf('BT %.2F Tz ET ', $horiz_scale); 4354 } 4355 $align = ''; 4356 $width = $w - (2 * $this->cMargin); 4357 } else { 4358 $stretch == 0; 4359 } 4360 if ($this->ColorFlag) { 4361 $s .= 'q '.$this->TextColor.' '; 4362 } 4363 // rendering mode 4364 $s .= sprintf('BT %d Tr %.2F w ET ', $this->textrendermode, $this->textstrokewidth); 4365 // count number of spaces 4366 $ns = substr_count($txt, ' '); 4367 // Justification 4368 $spacewidth = 0; 4369 if (($align == 'J') AND ($ns > 0)) { 4370 if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) { 4371 // get string width without spaces 4372 $width = $this->GetStringWidth(str_replace(' ', '', $txt)); 4373 // calculate average space width 4374 $spacewidth = -1000 * ($w - $width - (2 * $this->cMargin)) / ($ns?$ns:1) / $this->FontSize; 4375 // set word position to be used with TJ operator 4376 $txt2 = str_replace(chr(0).chr(32), ') '.sprintf('%.3F', $spacewidth).' (', $txt2); 4377 $unicode_justification = true; 4378 } else { 4379 // get string width 4380 $width = $txwidth; 4381 $spacewidth = (($w - $width - (2 * $this->cMargin)) / ($ns?$ns:1)) * $this->k; 4382 // set word spacing 4383 $rs .= sprintf('BT %.3F Tw ET ', $spacewidth); 4384 } 4385 $width = $w - (2 * $this->cMargin); 4386 } 4387 // replace carriage return characters 4388 $txt2 = str_replace("\r", ' ', $txt2); 4389 switch ($align) { 4390 case 'C': { 4391 $dx = ($w - $width) / 2; 4392 break; 4393 } 4394 case 'R': { 4395 if ($this->rtl) { 4396 $dx = $this->cMargin; 4397 } else { 4398 $dx = $w - $width - $this->cMargin; 4399 } 4400 break; 4401 } 4402 case 'L': { 4403 if ($this->rtl) { 4404 $dx = $w - $width - $this->cMargin; 4405 } else { 4406 $dx = $this->cMargin; 4407 } 4408 break; 4409 } 4410 case 'J': 4411 default: { 4412 $dx = $this->cMargin; 4413 break; 4414 } 4415 } 4416 if ($this->rtl) { 4417 $xdx = $this->x - $dx - $width; 4418 } else { 4419 $xdx = $this->x + $dx; 4420 } 4421 $xdk = $xdx * $k; 4422 // print text 4423 $s .= sprintf('BT %.2F %.2F Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2); 4424 if (isset($uniblock)) { 4425 // print overlapping characters as separate string 4426 $xshift = 0; // horizontal shift 4427 $ty = (($this->h - $basefonty + (0.2 * $this->FontSize)) * $k); 4428 $spw = (($w - $txwidth - (2 * $this->cMargin)) / ($ns?$ns:1)); 4429 foreach ($uniblock as $uk => $uniarr) { 4430 if (($uk % 2) == 0) { 4431 // x space to skip 4432 if ($spacewidth != 0) { 4433 // justification shift 4434 $xshift += (count(array_keys($uniarr, 32)) * $spw); 4435 } 4436 $xshift += $this->GetArrStringWidth($uniarr); // + shift justification 4437 } else { 4438 // character to print 4439 $topchr = $this->arrUTF8ToUTF16BE($uniarr, false); 4440 $topchr = $this->_escape($topchr); 4441 $s .= sprintf(' BT %.2F %.2F Td [(%s)] TJ ET', ($xdk + ($xshift * $k)), $ty, $topchr); 4442 } 4443 } 4444 } 4445 if ($this->underline) { 4446 $s .= ' '.$this->_dounderlinew($xdx, $basefonty, $width); 4447 } 4448 if ($this->linethrough) { 4449 $s .= ' '.$this->_dolinethroughw($xdx, $basefonty, $width); 4450 } 4451 if ($this->overline) { 4452 $s .= ' '.$this->_dooverlinew($xdx, $basefonty, $width); 4453 } 4454 if ($this->ColorFlag) { 4455 $s .= ' Q'; 4456 } 4457 if ($link) { 4458 $this->Link($xdx, $y + (($h - $this->FontSize)/2), $width, $this->FontSize, $link, $ns); 4459 } 4460 } 4461 // output cell 4462 if ($s) { 4463 // output cell 4464 $rs .= $s; 4465 // reset text stretching 4466 if ($stretch > 2) { 4467 //Reset character horizontal spacing 4468 $rs .= ' BT 0 Tc ET'; 4469 } elseif ($stretch > 0) { 4470 //Reset character horizontal scaling 4471 $rs .= ' BT 100 Tz ET'; 4472 } 4473 } 4474 // reset word spacing 4475 if (!(($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) AND ($align == 'J')) { 4476 $rs .= ' BT 0 Tw ET'; 4477 } 4478 $this->lasth = $h; 4479 if ($ln > 0) { 4480 //Go to the beginning of the next line 4481 $this->y = $y + $h; 4482 if ($ln == 1) { 4483 if ($this->rtl) { 4484 $this->x = $this->w - $this->rMargin; 4485 } else { 4486 $this->x = $this->lMargin; 4487 } 4488 } 4489 } else { 4490 // go left or right by case 4491 if ($this->rtl) { 4492 $this->x -= $w; 4493 } else { 4494 $this->x += $w; 4495 } 4496 } 4497 $gstyles = ''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor."\n"; 4498 $rs = $gstyles.$rs; 4499 return $rs; 4500 } 4501 4502 /** 4503 * This method allows printing text with line breaks. 4504 * They can be automatic (as soon as the text reaches the right border of the cell) or explicit (via the \n character). As many cells as necessary are output, one below the other.<br /> 4505 * Text can be aligned, centered or justified. The cell block can be framed and the background painted. 4506 * @param float $w Width of cells. If 0, they extend up to the right margin of the page. 4507 * @param float $h Cell minimum height. The cell extends automatically if needed. 4508 * @param string $txt String to print 4509 * @param mixed $border Indicates if borders must be drawn around the cell block. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> 4510 * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align</li><li>C: center</li><li>R: right align</li><li>J: justification (default value when $ishtml=false)</li></ul> 4511 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. 4512 * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right</li><li>1: to the beginning of the next line [DEFAULT]</li><li>2: below</li></ul> 4513 * @param float $x x position in user units 4514 * @param float $y y position in user units 4515 * @param boolean $reseth if true reset the last cell height (default true). 4516 * @param int $stretch stretch carachter mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if necessary</li><li>2 = forced horizontal scaling</li><li>3 = character spacing only if necessary</li><li>4 = forced character spacing</li></ul> 4517 * @param boolean $ishtml set to true if $txt is HTML content (default = false). 4518 * @param boolean $autopadding if true, uses internal padding and automatically adjust it to account for line width. 4519 * @param float $maxh maximum height. It should be >= $h and less then remaining space to the bottom of the page, or 0 for disable this feature. This feature works only when $ishtml=false. 4520 * @return int Return the number of cells or 1 for html mode. 4521 * @access public 4522 * @since 1.3 4523 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak() 4524 */ 4525 public function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=0, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0) { 4526 if ($this->empty_string($this->lasth) OR $reseth) { 4527 //set row height 4528 $this->lasth = $this->FontSize * $this->cell_height_ratio; 4529 } 4530 if (!$this->empty_string($y)) { 4531 $this->SetY($y); 4532 } else { 4533 $y = $this->GetY(); 4534 } 4535 // check for page break 4536 $this->checkPageBreak($h); 4537 $y = $this->GetY(); 4538 // get current page number 4539 $startpage = $this->page; 4540 if (!$this->empty_string($x)) { 4541 $this->SetX($x); 4542 } else { 4543 $x = $this->GetX(); 4544 } 4545 if ($this->empty_string($w) OR ($w <= 0)) { 4546 if ($this->rtl) { 4547 $w = $this->x - $this->lMargin; 4548 } else { 4549 $w = $this->w - $this->rMargin - $this->x; 4550 } 4551 } 4552 // store original margin values 4553 $lMargin = $this->lMargin; 4554 $rMargin = $this->rMargin; 4555 if ($this->rtl) { 4556 $this->SetRightMargin($this->w - $this->x); 4557 $this->SetLeftMargin($this->x - $w); 4558 } else { 4559 $this->SetLeftMargin($this->x); 4560 $this->SetRightMargin($this->w - $this->x - $w); 4561 } 4562 $starty = $this->y; 4563 if ($autopadding) { 4564 // Adjust internal padding 4565 if ($this->cMargin < ($this->LineWidth / 2)) { 4566 $this->cMargin = ($this->LineWidth / 2); 4567 } 4568 // Add top space if needed 4569 if (($this->lasth - $this->FontSize) < $this->LineWidth) { 4570 $this->y += $this->LineWidth / 2; 4571 } 4572 // add top padding 4573 $this->y += $this->cMargin; 4574 } 4575 if ($ishtml) { 4576 // ******* Write HTML text 4577 $this->writeHTML($txt, true, 0, $reseth, true, $align); 4578 $nl = 1; 4579 } else { 4580 // ******* Write text 4581 $nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false, true, $maxh); 4582 } 4583 if ($autopadding) { 4584 // add bottom padding 4585 $this->y += $this->cMargin; 4586 // Add bottom space if needed 4587 if (($this->lasth - $this->FontSize) < $this->LineWidth) { 4588 $this->y += $this->LineWidth / 2; 4589 } 4590 } 4591 // Get end-of-text Y position 4592 $currentY = $this->y; 4593 // get latest page number 4594 $endpage = $this->page; 4595 // check if a new page has been created 4596 if ($endpage > $startpage) { 4597 // design borders around HTML cells. 4598 for ($page=$startpage; $page <= $endpage; ++$page) { 4599 $this->setPage($page); 4600 if ($page == $startpage) { 4601 $this->y = $starty; // put cursor at the beginning of cell on the first page 4602 $h = $this->getPageHeight() - $starty - $this->getBreakMargin(); 4603 $cborder = $this->getBorderMode($border, $position='start'); 4604 } elseif ($page == $endpage) { 4605 $this->y = $this->tMargin; // put cursor at the beginning of last page 4606 $h = $currentY - $this->tMargin; 4607 $cborder = $this->getBorderMode($border, $position='end'); 4608 } else { 4609 $this->y = $this->tMargin; // put cursor at the beginning of the current page 4610 $h = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin(); 4611 $cborder = $this->getBorderMode($border, $position='middle'); 4612 } 4613 $nx = $x; 4614 // account for margin changes 4615 if ($page > $startpage) { 4616 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) { 4617 $nx = $x + ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']); 4618 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) { 4619 $nx = $x + ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']); 4620 } 4621 } 4622 $this->SetX($nx); 4623 $ccode = $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, false); 4624 if ($cborder OR $fill) { 4625 $pagebuff = $this->getPageBuffer($this->page); 4626 $pstart = substr($pagebuff, 0, $this->intmrk[$this->page]); 4627 $pend = substr($pagebuff, $this->intmrk[$this->page]); 4628 $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend); 4629 $this->intmrk[$this->page] += strlen($ccode."\n"); 4630 } 4631 } 4632 } else { 4633 $h = max($h, ($currentY - $y)); 4634 // put cursor at the beginning of text 4635 $this->SetY($y); 4636 $this->SetX($x); 4637 // design a cell around the text 4638 $ccode = $this->getCellCode($w, $h, '', $border, 1, '', $fill, '', 0, true); 4639 if ($border OR $fill) { 4640 if (end($this->transfmrk[$this->page]) !== false) { 4641 $pagemarkkey = key($this->transfmrk[$this->page]); 4642 $pagemark = &$this->transfmrk[$this->page][$pagemarkkey]; 4643 } elseif ($this->InFooter) { 4644 $pagemark = &$this->footerpos[$this->page]; 4645 } else { 4646 $pagemark = &$this->intmrk[$this->page]; 4647 } 4648 $pagebuff = $this->getPageBuffer($this->page); 4649 $pstart = substr($pagebuff, 0, $pagemark); 4650 $pend = substr($pagebuff, $pagemark); 4651 $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend); 4652 $pagemark += strlen($ccode."\n"); 4653 } 4654 } 4655 // Get end-of-cell Y position 4656 $currentY = $this->GetY(); 4657 // restore original margin values 4658 $this->SetLeftMargin($lMargin); 4659 $this->SetRightMargin($rMargin); 4660 if ($ln > 0) { 4661 //Go to the beginning of the next line 4662 $this->SetY($currentY); 4663 if ($ln == 2) { 4664 $this->SetX($x + $w); 4665 } 4666 } else { 4667 // go left or right by case 4668 $this->setPage($startpage); 4669 $this->y = $y; 4670 $this->SetX($x + $w); 4671 } 4672 $this->setContentMark(); 4673 return $nl; 4674 } 4675 4676 /** 4677 * Get the border mode accounting for multicell position (opens bottom side of multicell crossing pages) 4678 * @param mixed $border Indicates if borders must be drawn around the cell block. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> 4679 * @param string multicell position: 'start', 'middle', 'end' 4680 * @return border mode 4681 * @access protected 4682 * @since 4.4.002 (2008-12-09) 4683 */ 4684 protected function getBorderMode($border, $position='start') { 4685 if ((!$this->opencell) AND ($border == 1)) { 4686 return 1; 4687 } 4688 $cborder = ''; 4689 switch ($position) { 4690 case 'start': { 4691 if ($border == 1) { 4692 $cborder = 'LTR'; 4693 } else { 4694 if (!(false === strpos($border, 'L'))) { 4695 $cborder .= 'L'; 4696 } 4697 if (!(false === strpos($border, 'T'))) { 4698 $cborder .= 'T'; 4699 } 4700 if (!(false === strpos($border, 'R'))) { 4701 $cborder .= 'R'; 4702 } 4703 if ((!$this->opencell) AND (!(false === strpos($border, 'B')))) { 4704 $cborder .= 'B'; 4705 } 4706 } 4707 break; 4708 } 4709 case 'middle': { 4710 if ($border == 1) { 4711 $cborder = 'LR'; 4712 } else { 4713 if (!(false === strpos($border, 'L'))) { 4714 $cborder .= 'L'; 4715 } 4716 if ((!$this->opencell) AND (!(false === strpos($border, 'T')))) { 4717 $cborder .= 'T'; 4718 } 4719 if (!(false === strpos($border, 'R'))) { 4720 $cborder .= 'R'; 4721 } 4722 if ((!$this->opencell) AND (!(false === strpos($border, 'B')))) { 4723 $cborder .= 'B'; 4724 } 4725 } 4726 break; 4727 } 4728 case 'end': { 4729 if ($border == 1) { 4730 $cborder = 'LRB'; 4731 } else { 4732 if (!(false === strpos($border, 'L'))) { 4733 $cborder .= 'L'; 4734 } 4735 if ((!$this->opencell) AND (!(false === strpos($border, 'T')))) { 4736 $cborder .= 'T'; 4737 } 4738 if (!(false === strpos($border, 'R'))) { 4739 $cborder .= 'R'; 4740 } 4741 if (!(false === strpos($border, 'B'))) { 4742 $cborder .= 'B'; 4743 } 4744 } 4745 break; 4746 } 4747 default: { 4748 $cborder = $border; 4749 break; 4750 } 4751 } 4752 return $cborder; 4753 } 4754 4755 /** 4756 * This method returns the estimated number of lines required to print the text (not the real number just a quick estimation). 4757 * If you want o know the exact number of lines you have to use the following technique: 4758 * <pre> 4759 * // store current object 4760 * $pdf->startTransaction(); 4761 * // get the number of lines for multicell 4762 * $lines = $pdf->MultiCell($w, 0, $txt, 0, 'L', 0, 0, '', '', true, 0, false, true, 0); 4763 * // restore previous object 4764 * $pdf = $pdf->rollbackTransaction(); 4765 * </pre> 4766 * @param string $txt text to print 4767 * @param float $w width of cell. If 0, they extend up to the right margin of the page. 4768 * @return int Return the estimated number of lines. 4769 * @access public 4770 * @since 4.5.011 4771 */ 4772 public function getNumLines($txt, $w=0) { 4773 $lines = 0; 4774 if ($this->empty_string($w) OR ($w <= 0)) { 4775 if ($this->rtl) { 4776 $w = $this->x - $this->lMargin; 4777 } else { 4778 $w = $this->w - $this->rMargin - $this->x; 4779 } 4780 } 4781 // max column width 4782 $wmax = $w - (2 * $this->cMargin); 4783 // remove carriage returns 4784 $txt = str_replace("\r", '', $txt); 4785 // remove last newline (if any) 4786 if (substr($txt,-1) == "\n") { 4787 $txt = substr($txt, 0, -1); 4788 } 4789 // divide text in blocks 4790 $txtblocks = explode("\n", $txt); 4791 // for each text block 4792 foreach ($txtblocks as $block) { 4793 // estimate the number of lines 4794 $lines += $this->empty_string($block) ? 1 : (ceil($this->GetStringWidth($block) / $wmax)); 4795 } 4796 // return the number of lines 4797 return $lines; 4798 } 4799 4800 /** 4801 * This method prints text from the current position.<br /> 4802 * @param float $h Line height 4803 * @param string $txt String to print 4804 * @param mixed $link URL or identifier returned by AddLink() 4805 * @param int $fill Indicates if the background must be painted (1) or transparent (0). Default value: 0. 4806 * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul> 4807 * @param boolean $ln if true set cursor at the bottom of the line, otherwise set cursor at the top of the line. 4808 * @param int $stretch stretch carachter mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if necessary</li><li>2 = forced horizontal scaling</li><li>3 = character spacing only if necessary</li><li>4 = forced character spacing</li></ul> 4809 * @param boolean $firstline if true prints only the first line and return the remaining string. 4810 * @param boolean $firstblock if true the string is the starting of a line. 4811 * @param float $maxh maximum height. The remaining unprinted text will be returned. It should be >= $h and less then remaining space to the bottom of the page, or 0 for disable this feature. 4812 * @return mixed Return the number of cells or the remaining string if $firstline = true. 4813 * @access public 4814 * @since 1.5 4815 */ 4816 public function Write($h, $txt, $link='', $fill=0, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0) { 4817 if (strlen($txt) == 0) { 4818 $txt = ' '; 4819 } 4820 // remove carriage returns 4821 $s = str_replace("\r", '', $txt); 4822 // check if string contains arabic text 4823 if (preg_match(K_RE_PATTERN_ARABIC, $s)) { 4824 $arabic = true; 4825 } else { 4826 $arabic = false; 4827 } 4828 // check if string contains RTL text 4829 if ($arabic OR ($this->tmprtl == 'R') OR preg_match(K_RE_PATTERN_RTL, $txt)) { 4830 $rtlmode = true; 4831 } else { 4832 $rtlmode = false; 4833 } 4834 // get a char width 4835 $chrwidth = $this->GetCharWidth('.'); 4836 // get array of unicode values 4837 $chars = $this->UTF8StringToArray($s); 4838 // get array of chars 4839 $uchars = $this->UTF8ArrayToUniArray($chars); 4840 // get the number of characters 4841 $nb = count($chars); 4842 // replacement for SHY character (minus symbol) 4843 $shy_replacement = 45; 4844 $shy_replacement_char = $this->unichr($shy_replacement); 4845 // widht for SHY replacement 4846 $shy_replacement_width = $this->GetCharWidth($shy_replacement); 4847 // store current position 4848 $prevx = $this->x; 4849 $prevy = $this->y; 4850 // max Y 4851 $maxy = $this->y + $maxh - $h - (2 * $this->cMargin); 4852 // calculate remaining line width ($w) 4853 if ($this->rtl) { 4854 $w = $this->x - $this->lMargin; 4855 } else { 4856 $w = $this->w - $this->rMargin - $this->x; 4857 } 4858 // max column width 4859 $wmax = $w - (2 * $this->cMargin); 4860 if ((!$firstline) AND (($chrwidth > $wmax) OR ($this->GetCharWidth($chars[0]) > $wmax))) { 4861 // a single character do not fit on column 4862 return ''; 4863 } 4864 $i = 0; // character position 4865 $j = 0; // current starting position 4866 $sep = -1; // position of the last blank space 4867 $shy = false; // true if the last blank is a soft hypen (SHY) 4868 $l = 0; // current string length 4869 $nl = 0; //number of lines 4870 $linebreak = false; 4871 $pc = 0; // previous character 4872 // for each character 4873 while ($i < $nb) { 4874 if (($maxh > 0) AND ($this->y >= $maxy) ) { 4875 break; 4876 } 4877 //Get the current character 4878 $c = $chars[$i]; 4879 if ($c == 10) { // 10 = "\n" = new line 4880 //Explicit line break 4881 if ($align == 'J') { 4882 if ($this->rtl) { 4883 $talign = 'R'; 4884 } else { 4885 $talign = 'L'; 4886 } 4887 } else { 4888 $talign = $align; 4889 } 4890 $tmpstr = $this->UniArrSubString($uchars, $j, $i); 4891 if ($firstline) { 4892 $startx = $this->x; 4893 $tmparr = array_slice($chars, $j, ($i - $j)); 4894 if ($rtlmode) { 4895 $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl); 4896 } 4897 $linew = $this->GetArrStringWidth($tmparr); 4898 unset($tmparr); 4899 if ($this->rtl) { 4900 $this->endlinex = $startx - $linew; 4901 } else { 4902 $this->endlinex = $startx + $linew; 4903 } 4904 $w = $linew; 4905 $tmpcmargin = $this->cMargin; 4906 if ($maxh == 0) { 4907 $this->cMargin = 0; 4908 } 4909 } 4910 if ($firstblock AND $this->isRTLTextDir()) { 4911 $tmpstr = rtrim($tmpstr); 4912 } 4913 $this->Cell($w, $h, $tmpstr, 0, 1, $talign, $fill, $link, $stretch); 4914 unset($tmpstr); 4915 if ($firstline) { 4916 $this->cMargin = $tmpcmargin; 4917 return ($this->UniArrSubString($uchars, $i)); 4918 } 4919 ++$nl; 4920 $j = $i + 1; 4921 $l = 0; 4922 $sep = -1; 4923 $shy = false; 4924 // account for margin changes 4925 if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND (!$this->InFooter)) { 4926 // AcceptPageBreak() may be overriden on extended classed to include margin changes 4927 $this->AcceptPageBreak(); 4928 } 4929 $w = $this->getRemainingWidth(); 4930 $wmax = $w - (2 * $this->cMargin); 4931 } else { 4932 // 160 is the non-breaking space. 4933 // 173 is SHY (Soft Hypen). 4934 // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator. 4935 // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants. 4936 // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between. 4937 if (($c != 160) AND (($c == 173) OR preg_match($this->re_spaces, $this->unichr($c)))) { 4938 // update last blank space position 4939 $sep = $i; 4940 // check if is a SHY 4941 if ($c == 173) { 4942 $shy = true; 4943 if ($pc == 45) { 4944 $tmp_shy_replacement_width = 0; 4945 $tmp_shy_replacement_char = ''; 4946 } else { 4947 $tmp_shy_replacement_width = $shy_replacement_width; 4948 $tmp_shy_replacement_char = $shy_replacement_char; 4949 } 4950 } else { 4951 $shy = false; 4952 } 4953 } 4954 // update string length 4955 if ((($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) AND ($arabic)) { 4956 // with bidirectional algorithm some chars may be changed affecting the line length 4957 // *** very slow *** 4958 $l = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, ($i - $j)), '', $this->tmprtl)); 4959 } else { 4960 $l += $this->GetCharWidth($c); 4961 } 4962 if (($l > $wmax) OR (($c == 173) AND (($l + $tmp_shy_replacement_width) > $wmax)) ) { 4963 // we have reached the end of column 4964 if ($sep == -1) { 4965 // check if the line was already started 4966 if (($this->rtl AND ($this->x <= ($this->w - $this->rMargin - $chrwidth))) 4967 OR ((!$this->rtl) AND ($this->x >= ($this->lMargin + $chrwidth)))) { 4968 // print a void cell and go to next line 4969 $this->Cell($w, $h, '', 0, 1); 4970 $linebreak = true; 4971 if ($firstline) { 4972 return ($this->UniArrSubString($uchars, $j)); 4973 } 4974 } else { 4975 // truncate the word because do not fit on column 4976 $tmpstr = $this->UniArrSubString($uchars, $j, $i); 4977 if ($firstline) { 4978 $startx = $this->x; 4979 $tmparr = array_slice($chars, $j, ($i - $j)); 4980 if ($rtlmode) { 4981 $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl); 4982 } 4983 $linew = $this->GetArrStringWidth($tmparr); 4984 unset($tmparr); 4985 if ($this->rtl) { 4986 $this->endlinex = $startx - $linew; 4987 } else { 4988 $this->endlinex = $startx + $linew; 4989 } 4990 $w = $linew; 4991 $tmpcmargin = $this->cMargin; 4992 if ($maxh == 0) { 4993 $this->cMargin = 0; 4994 } 4995 } 4996 if ($firstblock AND $this->isRTLTextDir()) { 4997 $tmpstr = rtrim($tmpstr); 4998 } 4999 $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch); 5000 unset($tmpstr); 5001 if ($firstline) { 5002 $this->cMargin = $tmpcmargin; 5003 return ($this->UniArrSubString($uchars, $i)); 5004 } 5005 $j = $i; 5006 --$i; 5007 } 5008 } else { 5009 // word wrapping 5010 if ($this->rtl AND (!$firstblock)) { 5011 $endspace = 1; 5012 } else { 5013 $endspace = 0; 5014 } 5015 if ($shy) { 5016 // add hypen (minus symbol) at the end of the line 5017 $shy_width = $tmp_shy_replacement_width; 5018 if ($this->rtl) { 5019 $shy_char_left = $tmp_shy_replacement_char; 5020 $shy_char_right = ''; 5021 } else { 5022 $shy_char_left = ''; 5023 $shy_char_right = $tmp_shy_replacement_char; 5024 } 5025 } else { 5026 $shy_width = 0; 5027 $shy_char_left = ''; 5028 $shy_char_right = ''; 5029 } 5030 $tmpstr = $this->UniArrSubString($uchars, $j, ($sep + $endspace)); 5031 if ($firstline) { 5032 $startx = $this->x; 5033 $tmparr = array_slice($chars, $j, (($sep + $endspace) - $j)); 5034 if ($rtlmode) { 5035 $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl); 5036 } 5037 $linew = $this->GetArrStringWidth($tmparr); 5038 unset($tmparr); 5039 if ($this->rtl) { 5040 $this->endlinex = $startx - $linew - $shy_width; 5041 } else { 5042 $this->endlinex = $startx + $linew + $shy_width; 5043 } 5044 $w = $linew; 5045 $tmpcmargin = $this->cMargin; 5046 if ($maxh == 0) { 5047 $this->cMargin = 0; 5048 } 5049 } 5050 // print the line 5051 if ($firstblock AND $this->isRTLTextDir()) { 5052 $tmpstr = rtrim($tmpstr); 5053 } 5054 $this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch); 5055 unset($tmpstr); 5056 if ($firstline) { 5057 // return the remaining text 5058 $this->cMargin = $tmpcmargin; 5059 return ($this->UniArrSubString($uchars, ($sep + $endspace))); 5060 } 5061 $i = $sep; 5062 $sep = -1; 5063 $shy = false; 5064 $j = ($i+1); 5065 } 5066 // account for margin changes 5067 if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND (!$this->InFooter)) { 5068 // AcceptPageBreak() may be overriden on extended classed to include margin changes 5069 $this->AcceptPageBreak(); 5070 } 5071 $w = $this->getRemainingWidth(); 5072 $wmax = $w - (2 * $this->cMargin); 5073 if ($linebreak) { 5074 $linebreak = false; 5075 } else { 5076 ++$nl; 5077 $l = 0; 5078 } 5079 } 5080 } 5081 // save last character 5082 $pc = $c; 5083 ++$i; 5084 } // end while i < nb 5085 // print last substring (if any) 5086 if ($l > 0) { 5087 switch ($align) { 5088 case 'J': 5089 case 'C': { 5090 $w = $w; 5091 break; 5092 } 5093 case 'L': { 5094 if ($this->rtl) { 5095 $w = $w; 5096 } else { 5097 $w = $l; 5098 } 5099 break; 5100 } 5101 case 'R': { 5102 if ($this->rtl) { 5103 $w = $l; 5104 } else { 5105 $w = $w; 5106 } 5107 break; 5108 } 5109 default: { 5110 $w = $l; 5111 break; 5112 } 5113 } 5114 $tmpstr = $this->UniArrSubString($uchars, $j, $nb); 5115 if ($firstline) { 5116 $startx = $this->x; 5117 $tmparr = array_slice($chars, $j, ($nb - $j)); 5118 if ($rtlmode) { 5119 $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl); 5120 } 5121 $linew = $this->GetArrStringWidth($tmparr); 5122 unset($tmparr); 5123 if ($this->rtl) { 5124 $this->endlinex = $startx - $linew; 5125 } else { 5126 $this->endlinex = $startx + $linew; 5127 } 5128 $w = $linew; 5129 $tmpcmargin = $this->cMargin; 5130 if ($maxh == 0) { 5131 $this->cMargin = 0; 5132 } 5133 } 5134 if ($firstblock AND $this->isRTLTextDir()) { 5135 $tmpstr = rtrim($tmpstr); 5136 } 5137 $this->Cell($w, $h, $tmpstr, 0, $ln, $align, $fill, $link, $stretch); 5138 unset($tmpstr); 5139 if ($firstline) { 5140 $this->cMargin = $tmpcmargin; 5141 return ($this->UniArrSubString($uchars, $nb)); 5142 } 5143 ++$nl; 5144 } 5145 if ($firstline) { 5146 return ''; 5147 } 5148 return $nl; 5149 } 5150 5151 /** 5152 * Returns the remaining width between the current position and margins. 5153 * @return int Return the remaining width 5154 * @access protected 5155 */ 5156 protected function getRemainingWidth() { 5157 if ($this->rtl) { 5158 return ($this->x - $this->lMargin); 5159 } else { 5160 return ($this->w - $this->rMargin - $this->x); 5161 } 5162 } 5163 5164 /** 5165 * Extract a slice of the $strarr array and return it as string. 5166 * @param string $strarr The input array of characters. 5167 * @param int $start the starting element of $strarr. 5168 * @param int $end first element that will not be returned. 5169 * @return Return part of a string 5170 * @access public 5171 */ 5172 public function UTF8ArrSubString($strarr, $start='', $end='') { 5173 if (strlen($start) == 0) { 5174 $start = 0; 5175 } 5176 if (strlen($end) == 0) { 5177 $end = count($strarr); 5178 } 5179 $string = ''; 5180 for ($i=$start; $i < $end; ++$i) { 5181 $string .= $this->unichr($strarr[$i]); 5182 } 5183 return $string; 5184 } 5185 5186 /** 5187 * Extract a slice of the $uniarr array and return it as string. 5188 * @param string $uniarr The input array of characters. 5189 * @param int $start the starting element of $strarr. 5190 * @param int $end first element that will not be returned. 5191 * @return Return part of a string 5192 * @access public 5193 * @since 4.5.037 (2009-04-07) 5194 */ 5195 public function UniArrSubString($uniarr, $start='', $end='') { 5196 if (strlen($start) == 0) { 5197 $start = 0; 5198 } 5199 if (strlen($end) == 0) { 5200 $end = count($uniarr); 5201 } 5202 $string = ''; 5203 for ($i=$start; $i < $end; ++$i) { 5204 $string .= $uniarr[$i]; 5205 } 5206 return $string; 5207 } 5208 5209 /** 5210 * Convert an array of UTF8 values to array of unicode characters 5211 * @param string $ta The input array of UTF8 values. 5212 * @return Return array of unicode characters 5213 * @access public 5214 * @since 4.5.037 (2009-04-07) 5215 */ 5216 public function UTF8ArrayToUniArray($ta) { 5217 return array_map(array($this, 'unichr'), $ta); 5218 } 5219 5220 /** 5221 * Returns the unicode caracter specified by UTF-8 value 5222 * @param int $c UTF-8 value 5223 * @return Returns the specified character. 5224 * @author Miguel Perez, Nicola Asuni 5225 * @access public 5226 * @since 2.3.000 (2008-03-05) 5227 */ 5228 public function unichr($c) { 5229 if (!$this->isunicode) { 5230 return chr($c); 5231 } elseif ($c <= 0x7F) { 5232 // one byte 5233 return chr($c); 5234 } elseif ($c <= 0x7FF) { 5235 // two bytes 5236 return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F); 5237 } elseif ($c <= 0xFFFF) { 5238 // three bytes 5239 return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F); 5240 } elseif ($c <= 0x10FFFF) { 5241 // four bytes 5242 return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F); 5243 } else { 5244 return ''; 5245 } 5246 } 5247 5248 /** 5249 * Return the image type given the file name or array returned by getimagesize() function. 5250 * @param string $imgfile image file name 5251 * @param array $iminfo array of image information returned by getimagesize() function. 5252 * @return string image type 5253 * @since 4.8.017 (2009-11-27) 5254 */ 5255 public function getImageFileType($imgfile, $iminfo=array()) { 5256 if (isset($iminfo['mime']) AND !empty($iminfo['mime'])) { 5257 $mime = explode('/', $iminfo['mime']); 5258 if ((count($mime) > 1) AND ($mime[0] == 'image') AND (!empty($mime[1]))) { 5259 return trim($mime[1]); 5260 } 5261 } 5262 $type = ''; 5263 $fileinfo = pathinfo($imgfile); 5264 if (isset($fileinfo['extension']) AND (!$this->empty_string($fileinfo['extension']))) { 5265 $type = strtolower(trim($fileinfo['extension'])); 5266 } 5267 if ($type == 'jpg') { 5268 $type = 'jpeg'; 5269 } 5270 return $type; 5271 } 5272 5273 /** 5274 * Puts an image in the page. 5275 * The upper-left corner must be given. 5276 * The dimensions can be specified in different ways:<ul> 5277 * <li>explicit width and height (expressed in user unit)</li> 5278 * <li>one explicit dimension, the other being calculated automatically in order to keep the original proportions</li> 5279 * <li>no explicit dimension, in which case the image is put at 72 dpi</li></ul> 5280 * Supported formats are JPEG and PNG images whitout GD library and all images supported by GD: GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM; 5281 * The format can be specified explicitly or inferred from the file extension.<br /> 5282 * It is possible to put a link on the image.<br /> 5283 * Remark: if an image is used several times, only one copy will be embedded in the file.<br /> 5284 * @param string $file Name of the file containing the image. 5285 * @param float $x Abscissa of the upper-left corner. 5286 * @param float $y Ordinate of the upper-left corner. 5287 * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated. 5288 * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated. 5289 * @param string $type Image format. Possible values are (case insensitive): JPEG and PNG (whitout GD library) and all images supported by GD: GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM;. If not specified, the type is inferred from the file extension. 5290 * @param mixed $link URL or identifier returned by AddLink(). 5291 * @param string $align Indicates the alignment of the pointer next to image insertion relative to image height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul> 5292 * @param mixed $resize If true resize (reduce) the image to fit $w and $h (requires GD or ImageMagick library); if false do not resize; if 2 force resize in all cases (upscaling and downscaling). 5293 * @param int $dpi dot-per-inch resolution used on resize 5294 * @param string $palign Allows to center or align the image on the current line. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul> 5295 * @param boolean $ismask true if this image is a mask, false otherwise 5296 * @param mixed $imgmask image object returned by this function or false 5297 * @param mixed $border Indicates if borders must be drawn around the image. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> 5298 * @param boolean $fitbox If true scale image dimensions proportionally to fit within the ($w, $h) box. 5299 * @param boolean $hidden if true do not display the image. 5300 * @param boolean $fitonpage if true the image is resized to not exceed page dimensions. 5301 * @return image information 5302 * @access public 5303 * @since 1.1 5304 */ 5305 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) { 5306 if ($x === '') { 5307 $x = $this->x; 5308 } 5309 if ($y === '') { 5310 $y = $this->y; 5311 } 5312 // get image dimensions 5313 $imsize = @getimagesize($file); 5314 if ($imsize === FALSE) { 5315 // encode spaces on filename 5316 $file = str_replace(' ', '%20', $file); 5317 $imsize = @getimagesize($file); 5318 if ($imsize === FALSE) { 5319 if (($w > 0) AND ($h > 0)) { 5320 $pw = $this->getHTMLUnitToUnits($w, 0, $this->pdfunit, true) * $this->imgscale * $this->k; 5321 $ph = $this->getHTMLUnitToUnits($h, 0, $this->pdfunit, true) * $this->imgscale * $this->k; 5322 $imsize = array($pw, $ph); 5323 } else { 5324 $this->Error('[Image] Unable to get image width and height: '.$file); 5325 } 5326 } 5327 } 5328 // get original image width and height in pixels 5329 list($pixw, $pixh) = $imsize; 5330 // calculate image width and height on document 5331 if (($w <= 0) AND ($h <= 0)) { 5332 // convert image size to document unit 5333 $w = $this->pixelsToUnits($pixw); 5334 $h = $this->pixelsToUnits($pixh); 5335 } elseif ($w <= 0) { 5336 $w = $h * $pixw / $pixh; 5337 } elseif ($h <= 0) { 5338 $h = $w * $pixh / $pixw; 5339 } elseif ($fitbox AND ($w > 0) AND ($h > 0)) { 5340 // scale image dimensions proportionally to fit within the ($w, $h) box 5341 if ((($w * $pixh) / ($h * $pixw)) < 1) { 5342 $h = $w * $pixh / $pixw; 5343 } else { 5344 $w = $h * $pixw / $pixh; 5345 } 5346 } 5347 // Check whether we need a new page first as this does not fit 5348 $prev_x = $this->x; 5349 if ($this->checkPageBreak($h, $y)) { 5350 $y = $this->y; 5351 if ($this->rtl) { 5352 $x += ($prev_x - $this->x); 5353 } else { 5354 $x += ($this->x - $prev_x); 5355 } 5356 } 5357 // resize image to be contained on a single page 5358 if ($fitonpage) { 5359 $ratio_wh = $w / $h; 5360 if (($y + $h) > $this->PageBreakTrigger) { 5361 $h = $this->PageBreakTrigger - $y; 5362 $w = $h * $ratio_wh; 5363 } 5364 if (($x + $w) > ($this->w - $this->rMargin)) { 5365 $w = $this->w - $this->rMargin - $x; 5366 $h = $w / $ratio_wh; 5367 } 5368 } 5369 // calculate new minimum dimensions in pixels 5370 $neww = round($w * $this->k * $dpi / $this->dpi); 5371 $newh = round($h * $this->k * $dpi / $this->dpi); 5372 // check if resize is necessary (resize is used only to reduce the image) 5373 $newsize = ($neww * $newh); 5374 $pixsize = ($pixw * $pixh); 5375 if (intval($resize) == 2) { 5376 $resize = true; 5377 } elseif ($newsize >= $pixsize) { 5378 $resize = false; 5379 } 5380 // check if image has been already added on document 5381 $newimage = true; 5382 if (in_array($file, $this->imagekeys)) { 5383 $newimage = false; 5384 // get existing image data 5385 $info = $this->getImageBuffer($file); 5386 // check if the newer image is larger 5387 $oldsize = ($info['w'] * $info['h']); 5388 if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) { 5389 $newimage = true; 5390 } 5391 } 5392 if ($newimage) { 5393 //First use of image, get info 5394 if ($type == '') { 5395 $type = $this->getImageFileType($file, $imsize); 5396 } 5397 $mqr = $this->get_mqr(); 5398 $this->set_mqr(false); 5399 // Specific image handlers 5400 $mtd = '_parse'.$type; 5401 // GD image handler function 5402 $gdfunction = 'imagecreatefrom'.$type; 5403 $info = false; 5404 if ((method_exists($this, $mtd)) AND (!($resize AND function_exists($gdfunction)))) { 5405 // TCPDF image functions 5406 $info = $this->$mtd($file); 5407 if ($info == 'pngalpha') { 5408 return $this->ImagePngAlpha($file, $x, $y, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign); 5409 } 5410 } 5411 if (!$info) { 5412 if (function_exists($gdfunction)) { 5413 // GD library 5414 $img = $gdfunction($file); 5415 if ($resize) { 5416 $imgr = imagecreatetruecolor($neww, $newh); 5417 if (($type == 'gif') OR ($type == 'png')) { 5418 $imgr = $this->_setGDImageTransparency($imgr, $img); 5419 } 5420 imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh); 5421 if (($type == 'gif') OR ($type == 'png')) { 5422 $info = $this->_toPNG($imgr); 5423 } else { 5424 $info = $this->_toJPEG($imgr); 5425 } 5426 } else { 5427 if (($type == 'gif') OR ($type == 'png')) { 5428 $info = $this->_toPNG($img); 5429 } else { 5430 $info = $this->_toJPEG($img); 5431 } 5432 } 5433 } elseif (extension_loaded('imagick')) { 5434 // ImageMagick library 5435 $img = new Imagick(); 5436 if ($type == 'SVG') { 5437 // get SBG file content 5438 $svgimg = file_get_contents($file); 5439 // get width and height 5440 $regs = array(); 5441 if (preg_match('/<svg([^\>]*)>/si', $svgimg, $regs)) { 5442 $tmp = array(); 5443 if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) { 5444 $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false) * $dpi / 72; 5445 $svgimg = preg_replace('/[\s]+width[\s]*=[\s]*"[^"]*"/si', ' width="'.$ow.$this->pdfunit.'"', $svgimg); 5446 } 5447 $tmp = array(); 5448 if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) { 5449 $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false) * $dpi / 72; 5450 $svgimg = preg_replace('/[\s]+height[\s]*=[\s]*"[^"]*"/si', ' height="'.$oh.$this->pdfunit.'"', $svgimg); 5451 } 5452 $tmp = array(); 5453 if (!preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $regs[1], $tmp)) { 5454 $vbw = $ow * (72 / $dpi) * $this->imgscale * $this->k; 5455 $vbh = $oh * (72 / $dpi) * $this->imgscale * $this->k; 5456 $svgimg = preg_replace('/<svg/si', '<svg viewBox="0 0 '.$vbw.' '.$vbh.'"', $svgimg); 5457 } 5458 } 5459 $img->readImageBlob($svgimg); 5460 } else { 5461 $img->readImage($file); 5462 } 5463 if ($resize) { 5464 $img->resizeImage($neww, $newh, 10, 1, false); 5465 } 5466 $img->setCompressionQuality($this->jpeg_quality); 5467 $img->setImageFormat('jpeg'); 5468 $tempname = tempnam(K_PATH_CACHE, 'jpg_'); 5469 $img->writeImage($tempname); 5470 $info = $this->_parsejpeg($tempname); 5471 unlink($tempname); 5472 $img->destroy(); 5473 } else { 5474 return; 5475 } 5476 } 5477 if ($info === false) { 5478 //If false, we cannot process image 5479 return; 5480 } 5481 $this->set_mqr($mqr); 5482 if ($ismask) { 5483 // force grayscale 5484 $info['cs'] = 'DeviceGray'; 5485 } 5486 $info['i'] = $this->numimages; 5487 if (!in_array($file, $this->imagekeys)) { 5488 ++$info['i']; 5489 } 5490 if ($imgmask !== false) { 5491 $info['masked'] = $imgmask; 5492 } 5493 // add image to document 5494 $this->setImageBuffer($file, $info); 5495 } 5496 // set alignment 5497 $this->img_rb_y = $y + $h; 5498 // set alignment 5499 if ($this->rtl) { 5500 if ($palign == 'L') { 5501 $ximg = $this->lMargin; 5502 } elseif ($palign == 'C') { 5503 $ximg = ($this->w - $w) / 2; 5504 } elseif ($palign == 'R') { 5505 $ximg = $this->w - $this->rMargin - $w; 5506 } else { 5507 $ximg = $this->w - $x - $w; 5508 } 5509 $this->img_rb_x = $ximg; 5510 } else { 5511 if ($palign == 'L') { 5512 $ximg = $this->lMargin; 5513 } elseif ($palign == 'C') { 5514 $ximg = ($this->w - $w) / 2; 5515 } elseif ($palign == 'R') { 5516 $ximg = $this->w - $this->rMargin - $w; 5517 } else { 5518 $ximg = $x; 5519 } 5520 $this->img_rb_x = $ximg + $w; 5521 } 5522 if ($ismask OR $hidden) { 5523 // image is not displayed 5524 return $info['i']; 5525 } 5526 $xkimg = $ximg * $this->k; 5527 $this->_out(sprintf('q %.2F 0 0 %.2F %.2F %.2F cm /I%d Do Q', ($w * $this->k), ($h * $this->k), $xkimg, (($this->h - ($y + $h)) * $this->k), $info['i'])); 5528 if (!empty($border)) { 5529 $bx = $x; 5530 $by = $y; 5531 $this->x = $ximg; 5532 if ($this->rtl) { 5533 $this->x += $w; 5534 } 5535 $this->y = $y; 5536 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0); 5537 $this->x = $bx; 5538 $this->y = $by; 5539 } 5540 if ($link) { 5541 $this->Link($ximg, $y, $w, $h, $link, 0); 5542 } 5543 // set pointer to align the successive text/objects 5544 switch($align) { 5545 case 'T': { 5546 $this->y = $y; 5547 $this->x = $this->img_rb_x; 5548 break; 5549 } 5550 case 'M': { 5551 $this->y = $y + round($h/2); 5552 $this->x = $this->img_rb_x; 5553 break; 5554 } 5555 case 'B': { 5556 $this->y = $this->img_rb_y; 5557 $this->x = $this->img_rb_x; 5558 break; 5559 } 5560 case 'N': { 5561 $this->SetY($this->img_rb_y); 5562 break; 5563 } 5564 default:{ 5565 break; 5566 } 5567 } 5568 $this->endlinex = $this->img_rb_x; 5569 return $info['i']; 5570 } 5571 5572 /** 5573 * Sets the current active configuration setting of magic_quotes_runtime (if the set_magic_quotes_runtime function exist) 5574 * @param boolean $mqr FALSE for off, TRUE for on. 5575 * @since 4.6.025 (2009-08-17) 5576 */ 5577 public function set_mqr($mqr) { 5578 if(!defined('PHP_VERSION_ID')) { 5579 $version = PHP_VERSION; 5580 define('PHP_VERSION_ID', (($version{0} * 10000) + ($version{2} * 100) + $version{4})); 5581 } 5582 if (PHP_VERSION_ID < 50300) { 5583 @set_magic_quotes_runtime($mqr); 5584 } 5585 } 5586 5587 /** 5588 * Gets the current active configuration setting of magic_quotes_runtime (if the get_magic_quotes_runtime function exist) 5589 * @return Returns 0 if magic quotes runtime is off or get_magic_quotes_runtime doesn't exist, 1 otherwise. 5590 * @since 4.6.025 (2009-08-17) 5591 */ 5592 public function get_mqr() { 5593 if(!defined('PHP_VERSION_ID')) { 5594 $version = PHP_VERSION; 5595 define('PHP_VERSION_ID', (($version{0} * 10000) + ($version{2} * 100) + $version{4})); 5596 } 5597 if (PHP_VERSION_ID < 50300) { 5598 return @get_magic_quotes_runtime(); 5599 } 5600 return 0; 5601 } 5602 5603 /** 5604 * Convert the loaded image to a JPEG and then return a structure for the PDF creator. 5605 * This function requires GD library and write access to the directory defined on K_PATH_CACHE constant. 5606 * @param string $file Image file name. 5607 * @param image $image Image object. 5608 * return image JPEG image object. 5609 * @access protected 5610 */ 5611 protected function _toJPEG($image) { 5612 $tempname = tempnam(K_PATH_CACHE, 'jpg_'); 5613 imagejpeg($image, $tempname, $this->jpeg_quality); 5614 imagedestroy($image); 5615 $retvars = $this->_parsejpeg($tempname); 5616 // tidy up by removing temporary image 5617 unlink($tempname); 5618 return $retvars; 5619 } 5620 5621 /** 5622 * Convert the loaded image to a PNG and then return a structure for the PDF creator. 5623 * This function requires GD library and write access to the directory defined on K_PATH_CACHE constant. 5624 * @param string $file Image file name. 5625 * @param image $image Image object. 5626 * return image PNG image object. 5627 * @access protected 5628 * @since 4.9.016 (2010-04-20) 5629 */ 5630 protected function _toPNG($image) { 5631 $tempname = tempnam(K_PATH_CACHE, 'jpg_'); 5632 imagepng($image, $tempname); 5633 imagedestroy($image); 5634 $retvars = $this->_parsepng($tempname); 5635 // tidy up by removing temporary image 5636 unlink($tempname); 5637 return $retvars; 5638 } 5639 5640 /** 5641 * Set the transparency for the given GD image. 5642 * @param image $new_image GD image object 5643 * @param image $image GD image object. 5644 * return GD image object. 5645 * @access protected 5646 * @since 4.9.016 (2010-04-20) 5647 */ 5648 protected function _setGDImageTransparency($new_image, $image) { 5649 // transparency index 5650 $tid = imagecolortransparent($image); 5651 // default transparency color 5652 $tcol = array('red' => 255, 'green' => 255, 'blue' => 255); 5653 if ($tid >= 0) { 5654 // get the colors for the transparency index 5655 $tcol = imagecolorsforindex($image, $tid); 5656 } 5657 $tid = imagecolorallocate($new_image, $tcol['red'], $tcol['green'], $tcol['blue']); 5658 imagefill($new_image, 0, 0, $tid); 5659 imagecolortransparent($new_image, $tid); 5660 return $new_image; 5661 } 5662 5663 /** 5664 * Extract info from a JPEG file without using the GD library. 5665 * @param string $file image file to parse 5666 * @return array structure containing the image data 5667 * @access protected 5668 */ 5669 protected function _parsejpeg($file) { 5670 $a = getimagesize($file); 5671 if (empty($a)) { 5672 $this->Error('Missing or incorrect image file: '.$file); 5673 } 5674 if ($a[2] != 2) { 5675 $this->Error('Not a JPEG file: '.$file); 5676 } 5677 if ((!isset($a['channels'])) OR ($a['channels'] == 3)) { 5678 $colspace = 'DeviceRGB'; 5679 } elseif ($a['channels'] == 4) { 5680 $colspace = 'DeviceCMYK'; 5681 } else { 5682 $colspace = 'DeviceGray'; 5683 } 5684 $bpc = isset($a['bits']) ? $a['bits'] : 8; 5685 $data = file_get_contents($file); 5686 return array('w' => $a[0], 'h' => $a[1], 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data); 5687 } 5688 5689 /** 5690 * Extract info from a PNG file without using the GD library. 5691 * @param string $file image file to parse 5692 * @return array structure containing the image data 5693 * @access protected 5694 */ 5695 protected function _parsepng($file) { 5696 $f = fopen($file, 'rb'); 5697 if ($f === false) { 5698 $this->Error('Can\'t open image file: '.$file); 5699 } 5700 //Check signature 5701 if (fread($f, 8) != chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) { 5702 $this->Error('Not a PNG file: '.$file); 5703 } 5704 //Read header chunk 5705 fread($f, 4); 5706 if (fread($f, 4) != 'IHDR') { 5707 $this->Error('Incorrect PNG file: '.$file); 5708 } 5709 $w = $this->_freadint($f); 5710 $h = $this->_freadint($f); 5711 $bpc = ord(fread($f, 1)); 5712 if ($bpc > 8) { 5713 //$this->Error('16-bit depth not supported: '.$file); 5714 fclose($f); 5715 return false; 5716 } 5717 $ct = ord(fread($f, 1)); 5718 if ($ct == 0) { 5719 $colspace = 'DeviceGray'; 5720 } elseif ($ct == 2) { 5721 $colspace = 'DeviceRGB'; 5722 } elseif ($ct == 3) { 5723 $colspace = 'Indexed'; 5724 } else { 5725 // alpha channel 5726 fclose($f); 5727 return 'pngalpha'; 5728 } 5729 if (ord(fread($f, 1)) != 0) { 5730 //$this->Error('Unknown compression method: '.$file); 5731 fclose($f); 5732 return false; 5733 } 5734 if (ord(fread($f, 1)) != 0) { 5735 //$this->Error('Unknown filter method: '.$file); 5736 fclose($f); 5737 return false; 5738 } 5739 if (ord(fread($f, 1)) != 0) { 5740 //$this->Error('Interlacing not supported: '.$file); 5741 fclose($f); 5742 return false; 5743 } 5744 fread($f, 4); 5745 $parms = '/DecodeParms <</Predictor 15 /Colors '.($ct==2 ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w.'>>'; 5746 //Scan chunks looking for palette, transparency and image data 5747 $pal = ''; 5748 $trns = ''; 5749 $data = ''; 5750 do { 5751 $n = $this->_freadint($f); 5752 $type = fread($f, 4); 5753 if ($type == 'PLTE') { 5754 //Read palette 5755 $pal = $this->rfread($f, $n); 5756 fread($f, 4); 5757 } elseif ($type == 'tRNS') { 5758 //Read transparency info 5759 $t = $this->rfread($f, $n); 5760 if ($ct == 0) { 5761 $trns = array(ord(substr($t, 1, 1))); 5762 } elseif ($ct == 2) { 5763 $trns = array(ord(substr($t, 1, 1)), ord(substr($t, 3, 1)), ord(substr($t, 5, 1))); 5764 } else { 5765 $pos = strpos($t, chr(0)); 5766 if ($pos !== false) { 5767 $trns = array($pos); 5768 } 5769 } 5770 fread($f, 4); 5771 } elseif ($type == 'IDAT') { 5772 //Read image data block 5773 $data .= $this->rfread($f, $n); 5774 fread($f, 4); 5775 } elseif ($type == 'IEND') { 5776 break; 5777 } else { 5778 $this->rfread($f, $n + 4); 5779 } 5780 } while ($n); 5781 if (($colspace == 'Indexed') AND (empty($pal))) { 5782 //$this->Error('Missing palette in '.$file); 5783 fclose($f); 5784 return false; 5785 } 5786 fclose($f); 5787 return array('w' => $w, 'h' => $h, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'parms' => $parms, 'pal' => $pal, 'trns' => $trns, 'data' => $data); 5788 } 5789 5790 /** 5791 * Binary-safe and URL-safe file read. 5792 * Reads up to length bytes from the file pointer referenced by handle. Reading stops as soon as one of the following conditions is met: length bytes have been read; EOF (end of file) is reached. 5793 * @param resource $handle 5794 * @param int $length 5795 * @return Returns the read string or FALSE in case of error. 5796 * @author Nicola Asuni 5797 * @access protected 5798 * @since 4.5.027 (2009-03-16) 5799 */ 5800 protected function rfread($handle, $length) { 5801 $data = fread($handle, $length); 5802 if ($data === false) { 5803 return false; 5804 } 5805 $rest = $length - strlen($data); 5806 if ($rest > 0) { 5807 $data .= $this->rfread($handle, $rest); 5808 } 5809 return $data; 5810 } 5811 5812 /** 5813 * Extract info from a PNG image with alpha channel using the GD library. 5814 * @param string $file Name of the file containing the image. 5815 * @param float $x Abscissa of the upper-left corner. 5816 * @param float $y Ordinate of the upper-left corner. 5817 * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated. 5818 * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated. 5819 * @param string $type Image format. Possible values are (case insensitive): JPEG and PNG (whitout GD library) and all images supported by GD: GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM;. If not specified, the type is inferred from the file extension. 5820 * @param mixed $link URL or identifier returned by AddLink(). 5821 * @param string $align Indicates the alignment of the pointer next to image insertion relative to image height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul> 5822 * @param boolean $resize If true resize (reduce) the image to fit $w and $h (requires GD library). 5823 * @param int $dpi dot-per-inch resolution used on resize 5824 * @param string $palign Allows to center or align the image on the current line. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul> 5825 * @author Valentin Schmidt, Nicola Asuni 5826 * @access protected 5827 * @since 4.3.007 (2008-12-04) 5828 * @see Image() 5829 */ 5830 protected function ImagePngAlpha($file, $x='', $y='', $w=0, $h=0, $type='', $link='', $align='', $resize=false, $dpi=300, $palign='') { 5831 // get image size 5832 list($wpx, $hpx) = getimagesize($file); 5833 // generate images 5834 $img = imagecreatefrompng($file); 5835 $imgalpha = imagecreate($wpx, $hpx); 5836 // generate gray scale pallete 5837 for ($c = 0; $c < 256; ++$c) { 5838 ImageColorAllocate($imgalpha, $c, $c, $c); 5839 } 5840 // extract alpha channel 5841 for ($xpx = 0; $xpx < $wpx; ++$xpx) { 5842 for ($ypx = 0; $ypx < $hpx; ++$ypx) { 5843 $colorindex = imagecolorat($img, $xpx, $ypx); 5844 $col = imagecolorsforindex($img, $colorindex); 5845 imagesetpixel($imgalpha, $xpx, $ypx, $this->getGDgamma((127 - $col['alpha']) * 255 / 127)); 5846 } 5847 } 5848 // create temp alpha file 5849 $tempfile_alpha = tempnam(K_PATH_CACHE, 'mska_'); 5850 imagepng($imgalpha, $tempfile_alpha); 5851 imagedestroy($imgalpha); 5852 // extract image without alpha channel 5853 $imgplain = imagecreatetruecolor($wpx, $hpx); 5854 imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx); 5855 // create temp image file 5856 $tempfile_plain = tempnam(K_PATH_CACHE, 'mskp_'); 5857 imagepng($imgplain, $tempfile_plain); 5858 imagedestroy($imgplain); 5859 // embed mask image 5860 $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false); 5861 // embed image, masked with previously embedded mask 5862 $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask); 5863 // remove temp files 5864 unlink($tempfile_alpha); 5865 unlink($tempfile_plain); 5866 } 5867 5868 /** 5869 * Correct the gamma value to be used with GD library 5870 * @param float $v the gamma value to be corrected 5871 * @access protected 5872 * @since 4.3.007 (2008-12-04) 5873 */ 5874 protected function getGDgamma($v) { 5875 return (pow(($v / 255), 2.2) * 255); 5876 } 5877 5878 /** 5879 * Performs a line break. 5880 * The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter. 5881 * @param float $h The height of the break. By default, the value equals the height of the last printed cell. 5882 * @param boolean $cell if true add a cMargin to the x coordinate 5883 * @access public 5884 * @since 1.0 5885 * @see Cell() 5886 */ 5887 public function Ln($h='', $cell=false) { 5888 if (($this->num_columns > 0) 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'])) { 5889 // revove vertical space from the top of the column 5890 return; 5891 } 5892 if ($cell) { 5893 $cellmargin = $this->cMargin; 5894 } else { 5895 $cellmargin = 0; 5896 } 5897 if ($this->rtl) { 5898 $this->x = $this->w - $this->rMargin - $cellmargin; 5899 } else { 5900 $this->x = $this->lMargin + $cellmargin; 5901 } 5902 if (is_string($h)) { 5903 $this->y += $this->lasth; 5904 } else { 5905 $this->y += $h; 5906 } 5907 $this->newline = true; 5908 } 5909 5910 /** 5911 * Returns the relative X value of current position. 5912 * The value is relative to the left border for LTR languages and to the right border for RTL languages. 5913 * @return float 5914 * @access public 5915 * @since 1.2 5916 * @see SetX(), GetY(), SetY() 5917 */ 5918 public function GetX() { 5919 //Get x position 5920 if ($this->rtl) { 5921 return ($this->w - $this->x); 5922 } else { 5923 return $this->x; 5924 } 5925 } 5926 5927 /** 5928 * Returns the absolute X value of current position. 5929 * @return float 5930 * @access public 5931 * @since 1.2 5932 * @see SetX(), GetY(), SetY() 5933 */ 5934 public function GetAbsX() { 5935 return $this->x; 5936 } 5937 5938 /** 5939 * Returns the ordinate of the current position. 5940 * @return float 5941 * @access public 5942 * @since 1.0 5943 * @see SetY(), GetX(), SetX() 5944 */ 5945 public function GetY() { 5946 return $this->y; 5947 } 5948 5949 /** 5950 * Defines the abscissa of the current position. 5951 * If the passed value is negative, it is relative to the right of the page (or left if language is RTL). 5952 * @param float $x The value of the abscissa. 5953 * @param boolean $rtloff if true always uses the page top-left corner as origin of axis. 5954 * @access public 5955 * @since 1.2 5956 * @see GetX(), GetY(), SetY(), SetXY() 5957 */ 5958 public function SetX($x, $rtloff=false) { 5959 if (!$rtloff AND $this->rtl) { 5960 if ($x >= 0) { 5961 $this->x = $this->w - $x; 5962 } else { 5963 $this->x = abs($x); 5964 } 5965 } else { 5966 if ($x >= 0) { 5967 $this->x = $x; 5968 } else { 5969 $this->x = $this->w + $x; 5970 } 5971 } 5972 if ($this->x < 0) { 5973 $this->x = 0; 5974 } 5975 if ($this->x > $this->w) { 5976 $this->x = $this->w; 5977 } 5978 } 5979 5980 /** 5981 * Moves the current abscissa back to the left margin and sets the ordinate. 5982 * If the passed value is negative, it is relative to the bottom of the page. 5983 * @param float $y The value of the ordinate. 5984 * @param bool $resetx if true (default) reset the X position. 5985 * @param boolean $rtloff if true always uses the page top-left corner as origin of axis. 5986 * @access public 5987 * @since 1.0 5988 * @see GetX(), GetY(), SetY(), SetXY() 5989 */ 5990 public function SetY($y, $resetx=true, $rtloff=false) { 5991 if ($resetx) { 5992 //reset x 5993 if (!$rtloff AND $this->rtl) { 5994 $this->x = $this->w - $this->rMargin; 5995 } else { 5996 $this->x = $this->lMargin; 5997 } 5998 } 5999 if ($y >= 0) { 6000 $this->y = $y; 6001 } else { 6002 $this->y = $this->h + $y; 6003 } 6004 if ($this->y < 0) { 6005 $this->y = 0; 6006 } 6007 if ($this->y > $this->h) { 6008 $this->y = $this->h; 6009 } 6010 } 6011 6012 /** 6013 * Defines the abscissa and ordinate of the current position. 6014 * If the passed values are negative, they are relative respectively to the right and bottom of the page. 6015 * @param float $x The value of the abscissa. 6016 * @param float $y The value of the ordinate. 6017 * @param boolean $rtloff if true always uses the page top-left corner as origin of axis. 6018 * @access public 6019 * @since 1.2 6020 * @see SetX(), SetY() 6021 */ 6022 public function SetXY($x, $y, $rtloff=false) { 6023 $this->SetY($y, false, $rtloff); 6024 $this->SetX($x, $rtloff); 6025 } 6026 6027 /** 6028 * Send the document to a given destination: string, local file or browser. 6029 * In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.<br /> 6030 * The method first calls Close() if necessary to terminate the document. 6031 * @param string $name The name of the file when saved. Note that special characters are removed and blanks characters are replaced with the underscore character. 6032 * @param string $dest Destination where to send the document. It can take one of the following values:<ul><li>I: send the file inline to the browser (default). The plug-in is used if available. The name given by name is used when one selects the "Save as" option on the link generating the PDF.</li><li>D: send to the browser and force a file download with the name given by name.</li><li>F: save to a local server file with the name given by name.</li><li>S: return the document as a string. name is ignored.</li><li>FI: equivalent to F + I option</li><li>FD: equivalent to F + D option</li></ul> 6033 * @access public 6034 * @since 1.0 6035 * @see Close() 6036 */ 6037 public function Output($name='doc.pdf', $dest='I') { 6038 //Output PDF to some destination 6039 //Finish document if necessary 6040 $this->lastpage(); 6041 if ($this->state < 3) { 6042 $this->Close(); 6043 } 6044 //Normalize parameters 6045 if (is_bool($dest)) { 6046 $dest = $dest ? 'D' : 'F'; 6047 } 6048 $dest = strtoupper($dest); 6049 if ($dest != 'F') { 6050 $name = preg_replace('/[\s]+/', '_', $name); 6051 $name = preg_replace('/[^a-zA-Z0-9_\.-]/', '', $name); 6052 } 6053 if ($this->sign) { 6054 // *** apply digital signature to the document *** 6055 // get the document content 6056 $pdfdoc = $this->getBuffer(); 6057 // remove last newline 6058 $pdfdoc = substr($pdfdoc, 0, -1); 6059 // Remove the original buffer 6060 if (isset($this->diskcache) AND $this->diskcache) { 6061 // remove buffer file from cache 6062 unlink($this->buffer); 6063 } 6064 unset($this->buffer); 6065 // remove filler space 6066 $byterange_string_len = strlen($this->byterange_string); 6067 // define the ByteRange 6068 $byte_range = array(); 6069 $byte_range[0] = 0; 6070 $byte_range[1] = strpos($pdfdoc, $this->byterange_string) + $byterange_string_len + 10; 6071 $byte_range[2] = $byte_range[1] + $this->signature_max_length + 2; 6072 $byte_range[3] = strlen($pdfdoc) - $byte_range[2]; 6073 $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).substr($pdfdoc, $byte_range[2]); 6074 // replace the ByteRange 6075 $byterange = sprintf('/ByteRange[0 %u %u %u]', $byte_range[1], $byte_range[2], $byte_range[3]); 6076 $byterange .= str_repeat(' ', ($byterange_string_len - strlen($byterange))); 6077 $pdfdoc = str_replace($this->byterange_string, $byterange, $pdfdoc); 6078 // write the document to a temporary folder 6079 $tempdoc = tempnam(K_PATH_CACHE, 'tmppdf_'); 6080 $f = fopen($tempdoc, 'wb'); 6081 if (!$f) { 6082 $this->Error('Unable to create temporary file: '.$tempdoc); 6083 } 6084 $pdfdoc_length = strlen($pdfdoc); 6085 fwrite($f, $pdfdoc, $pdfdoc_length); 6086 fclose($f); 6087 // get digital signature via openssl library 6088 $tempsign = tempnam(K_PATH_CACHE, 'tmpsig_'); 6089 if (empty($this->signature_data['extracerts'])) { 6090 openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED); 6091 } else { 6092 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']); 6093 } 6094 unlink($tempdoc); 6095 // read signature 6096 $signature = file_get_contents($tempsign, false, null, $pdfdoc_length); 6097 unlink($tempsign); 6098 // extract signature 6099 $signature = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13)); 6100 $tmparr = explode("\n\n", $signature); 6101 $signature = $tmparr[1]; 6102 unset($tmparr); 6103 // decode signature 6104 $signature = base64_decode(trim($signature)); 6105 // convert signature to hex 6106 $signature = current(unpack('H*', $signature)); 6107 $signature = str_pad($signature, $this->signature_max_length, '0'); 6108 // Add signature to the document 6109 $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).'<'.$signature.'>'.substr($pdfdoc, ($byte_range[1])); 6110 $this->diskcache = false; 6111 $this->buffer = &$pdfdoc; 6112 $this->bufferlen = strlen($pdfdoc); 6113 } 6114 switch($dest) { 6115 case 'I': { 6116 // Send PDF to the standard output 6117 if (ob_get_contents()) { 6118 $this->Error('Some data has already been output, can\'t send PDF file'); 6119 } 6120 if (php_sapi_name() != 'cli') { 6121 //We send to a browser 6122 header('Content-Type: application/pdf'); 6123 if (headers_sent()) { 6124 $this->Error('Some data has already been output to browser, can\'t send PDF file'); 6125 } 6126 header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1 6127 header('Pragma: public'); 6128 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past 6129 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); 6130 header('Content-Length: '.$this->bufferlen); 6131 header('Content-Disposition: inline; filename="'.basename($name).'";'); 6132 } 6133 echo $this->getBuffer(); 6134 break; 6135 } 6136 case 'D': { 6137 // Download PDF as file 6138 if (ob_get_contents()) { 6139 $this->Error('Some data has already been output, can\'t send PDF file'); 6140 } 6141 header('Content-Description: File Transfer'); 6142 if (headers_sent()) { 6143 $this->Error('Some data has already been output to browser, can\'t send PDF file'); 6144 } 6145 header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1 6146 header('Pragma: public'); 6147 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past 6148 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); 6149 // force download dialog 6150 header('Content-Type: application/force-download'); 6151 header('Content-Type: application/octet-stream', false); 6152 header('Content-Type: application/download', false); 6153 header('Content-Type: application/pdf', false); 6154 // use the Content-Disposition header to supply a recommended filename 6155 header('Content-Disposition: attachment; filename="'.basename($name).'";'); 6156 header('Content-Transfer-Encoding: binary'); 6157 header('Content-Length: '.$this->bufferlen); 6158 echo $this->getBuffer(); 6159 break; 6160 } 6161 case 'F': 6162 case 'FI': 6163 case 'FD': { 6164 // Save PDF to a local file 6165 if ($this->diskcache) { 6166 copy($this->buffer, $name); 6167 } else { 6168 $f = fopen($name, 'wb'); 6169 if (!$f) { 6170 $this->Error('Unable to create output file: '.$name); 6171 } 6172 fwrite($f, $this->getBuffer(), $this->bufferlen); 6173 fclose($f); 6174 } 6175 if ($dest == 'FI') { 6176 // send headers to browser 6177 header('Content-Type: application/pdf'); 6178 header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1 6179 header('Pragma: public'); 6180 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past 6181 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); 6182 header('Content-Length: '.filesize($name)); 6183 header('Content-Disposition: inline; filename="'.basename($name).'";'); 6184 // send document to the browser 6185 echo file_get_contents($name); 6186 } elseif ($dest == 'FD') { 6187 // send headers to browser 6188 if (ob_get_contents()) { 6189 $this->Error('Some data has already been output, can\'t send PDF file'); 6190 } 6191 header('Content-Description: File Transfer'); 6192 if (headers_sent()) { 6193 $this->Error('Some data has already been output to browser, can\'t send PDF file'); 6194 } 6195 header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1 6196 header('Pragma: public'); 6197 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past 6198 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); 6199 // force download dialog 6200 header('Content-Type: application/force-download'); 6201 header('Content-Type: application/octet-stream', false); 6202 header('Content-Type: application/download', false); 6203 header('Content-Type: application/pdf', false); 6204 // use the Content-Disposition header to supply a recommended filename 6205 header('Content-Disposition: attachment; filename="'.basename($name).'";'); 6206 header('Content-Transfer-Encoding: binary'); 6207 header('Content-Length: '.filesize($name)); 6208 // send document to the browser 6209 echo file_get_contents($name); 6210 } 6211 break; 6212 } 6213 case 'S': { 6214 // Returns PDF as a string 6215 return $this->getBuffer(); 6216 } 6217 default: { 6218 $this->Error('Incorrect output destination: '.$dest); 6219 } 6220 } 6221 return ''; 6222 } 6223 6224 /** 6225 * Unset all class variables except the following critical variables: internal_encoding, state, bufferlen, buffer and diskcache. 6226 * @param boolean $destroyall if true destroys all class variables, otherwise preserves critical variables. 6227 * @param boolean $preserve_objcopy if true preserves the objcopy variable 6228 * @access public 6229 * @since 4.5.016 (2009-02-24) 6230 */ 6231 public function _destroy($destroyall=false, $preserve_objcopy=false) { 6232 if ($destroyall AND isset($this->diskcache) AND $this->diskcache AND (!$preserve_objcopy) AND (!$this->empty_string($this->buffer))) { 6233 // remove buffer file from cache 6234 unlink($this->buffer); 6235 } 6236 foreach (array_keys(get_object_vars($this)) as $val) { 6237 if ($destroyall OR ( 6238 ($val != 'internal_encoding') 6239 AND ($val != 'state') 6240 AND ($val != 'bufferlen') 6241 AND ($val != 'buffer') 6242 AND ($val != 'diskcache') 6243 AND ($val != 'sign') 6244 AND ($val != 'signature_data') 6245 AND ($val != 'signature_max_length') 6246 AND ($val != 'byterange_string') 6247 )) { 6248 if ((!$preserve_objcopy OR ($val != 'objcopy')) AND isset($this->$val)) { 6249 unset($this->$val); 6250 } 6251 } 6252 } 6253 } 6254 6255 /** 6256 * Check for locale-related bug 6257 * @access protected 6258 */ 6259 protected function _dochecks() { 6260 //Check for locale-related bug 6261 if (1.1 == 1) { 6262 $this->Error('Don\'t alter the locale before including class file'); 6263 } 6264 //Check for decimal separator 6265 if (sprintf('%.1F', 1.0) != '1.0') { 6266 setlocale(LC_NUMERIC, 'C'); 6267 } 6268 } 6269 6270 /** 6271 * Return fonts path 6272 * @return string 6273 * @access protected 6274 */ 6275 protected function _getfontpath() { 6276 if (!defined('K_PATH_FONTS') AND is_dir(dirname(__FILE__).'/fonts')) { 6277 define('K_PATH_FONTS', dirname(__FILE__).'/fonts/'); 6278 } 6279 return defined('K_PATH_FONTS') ? K_PATH_FONTS : ''; 6280 } 6281 6282 /** 6283 * Output pages. 6284 * @access protected 6285 */ 6286 protected function _putpages() { 6287 $nb = $this->numpages; 6288 if (!empty($this->AliasNbPages)) { 6289 $nbs = $this->formatPageNumber($nb); 6290 $nbu = $this->UTF8ToUTF16BE($nbs, false); // replacement for unicode font 6291 $alias_a = $this->_escape($this->AliasNbPages); 6292 $alias_au = $this->_escape('{'.$this->AliasNbPages.'}'); 6293 if ($this->isunicode) { 6294 $alias_b = $this->_escape($this->UTF8ToLatin1($this->AliasNbPages)); 6295 $alias_bu = $this->_escape($this->UTF8ToLatin1('{'.$this->AliasNbPages.'}')); 6296 $alias_c = $this->_escape($this->utf8StrRev($this->AliasNbPages, false, $this->tmprtl)); 6297 $alias_cu = $this->_escape($this->utf8StrRev('{'.$this->AliasNbPages.'}', false, $this->tmprtl)); 6298 } 6299 } 6300 if (!empty($this->AliasNumPage)) { 6301 $alias_pa = $this->_escape($this->AliasNumPage); 6302 $alias_pau = $this->_escape('{'.$this->AliasNumPage.'}'); 6303 if ($this->isunicode) { 6304 $alias_pb = $this->_escape($this->UTF8ToLatin1($this->AliasNumPage)); 6305 $alias_pbu = $this->_escape($this->UTF8ToLatin1('{'.$this->AliasNumPage.'}')); 6306 $alias_pc = $this->_escape($this->utf8StrRev($this->AliasNumPage, false, $this->tmprtl)); 6307 $alias_pcu = $this->_escape($this->utf8StrRev('{'.$this->AliasNumPage.'}', false, $this->tmprtl)); 6308 } 6309 } 6310 $pagegroupnum = 0; 6311 $filter = ($this->compress) ? '/Filter /FlateDecode ' : ''; 6312 for ($n=1; $n <= $nb; ++$n) { 6313 $temppage = $this->getPageBuffer($n); 6314 if (!empty($this->pagegroups)) { 6315 if(isset($this->newpagegroup[$n])) { 6316 $pagegroupnum = 0; 6317 } 6318 ++$pagegroupnum; 6319 foreach ($this->pagegroups as $k => $v) { 6320 // replace total pages group numbers 6321 $vs = $this->formatPageNumber($v); 6322 $vu = $this->UTF8ToUTF16BE($vs, false); 6323 $alias_ga = $this->_escape($k); 6324 $alias_gau = $this->_escape('{'.$k.'}'); 6325 if ($this->isunicode) { 6326 $alias_gb = $this->_escape($this->UTF8ToLatin1($k)); 6327 $alias_gbu = $this->_escape($this->UTF8ToLatin1('{'.$k.'}')); 6328 $alias_gc = $this->_escape($this->utf8StrRev($k, false, $this->tmprtl)); 6329 $alias_gcu = $this->_escape($this->utf8StrRev('{'.$k.'}', false, $this->tmprtl)); 6330 } 6331 $temppage = str_replace($alias_gau, $vu, $temppage); 6332 if ($this->isunicode) { 6333 $temppage = str_replace($alias_gbu, $vu, $temppage); 6334 $temppage = str_replace($alias_gcu, $vu, $temppage); 6335 $temppage = str_replace($alias_gb, $vs, $temppage); 6336 $temppage = str_replace($alias_gc, $vs, $temppage); 6337 } 6338 $temppage = str_replace($alias_ga, $vs, $temppage); 6339 // replace page group numbers 6340 $pvs = $this->formatPageNumber($pagegroupnum); 6341 $pvu = $this->UTF8ToUTF16BE($pvs, false); 6342 $pk = str_replace('{nb', '{pnb', $k); 6343 $alias_pga = $this->_escape($pk); 6344 $alias_pgau = $this->_escape('{'.$pk.'}'); 6345 if ($this->isunicode) { 6346 $alias_pgb = $this->_escape($this->UTF8ToLatin1($pk)); 6347 $alias_pgbu = $this->_escape($this->UTF8ToLatin1('{'.$pk.'}')); 6348 $alias_pgc = $this->_escape($this->utf8StrRev($pk, false, $this->tmprtl)); 6349 $alias_pgcu = $this->_escape($this->utf8StrRev('{'.$pk.'}', false, $this->tmprtl)); 6350 } 6351 $temppage = str_replace($alias_pgau, $pvu, $temppage); 6352 if ($this->isunicode) { 6353 $temppage = str_replace($alias_pgbu, $pvu, $temppage); 6354 $temppage = str_replace($alias_pgcu, $pvu, $temppage); 6355 $temppage = str_replace($alias_pgb, $pvs, $temppage); 6356 $temppage = str_replace($alias_pgc, $pvs, $temppage); 6357 } 6358 $temppage = str_replace($alias_pga, $pvs, $temppage); 6359 } 6360 } 6361 if (!empty($this->AliasNbPages)) { 6362 // replace total pages number 6363 $temppage = str_replace($alias_au, $nbu, $temppage); 6364 if ($this->isunicode) { 6365 $temppage = str_replace($alias_bu, $nbu, $temppage); 6366 $temppage = str_replace($alias_cu, $nbu, $temppage); 6367 $temppage = str_replace($alias_b, $nbs, $temppage); 6368 $temppage = str_replace($alias_c, $nbs, $temppage); 6369 } 6370 $temppage = str_replace($alias_a, $nbs, $temppage); 6371 } 6372 if (!empty($this->AliasNumPage)) { 6373 // replace page number 6374 $pnbs = $this->formatPageNumber($n); 6375 $pnbu = $this->UTF8ToUTF16BE($pnbs, false); // replacement for unicode font 6376 $temppage = str_replace($alias_pau, $pnbu, $temppage); 6377 if ($this->isunicode) { 6378 $temppage = str_replace($alias_pbu, $pnbu, $temppage); 6379 $temppage = str_replace($alias_pcu, $pnbu, $temppage); 6380 $temppage = str_replace($alias_pb, $pnbs, $temppage); 6381 $temppage = str_replace($alias_pc, $pnbs, $temppage); 6382 } 6383 $temppage = str_replace($alias_pa, $pnbs, $temppage); 6384 } 6385 $temppage = str_replace($this->epsmarker, '', $temppage); 6386 //Page 6387 $this->page_obj_id[$n] = $this->_newobj(); 6388 $out = '<</Type /Page'; 6389 $out .= ' /Parent 1 0 R'; 6390 $out .= ' '.sprintf('/MediaBox [0 0 %.2F %.2F]', $this->pagedim[$n]['w'], $this->pagedim[$n]['h']); 6391 $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceRGB >>'; 6392 $out .= ' /Resources 2 0 R'; 6393 $this->_out($out); 6394 $this->_putannotsrefs($n); 6395 $this->_out('/Contents '.($this->n + 1).' 0 R>> endobj'); 6396 //Page content 6397 $p = ($this->compress) ? gzcompress($temppage) : $temppage; 6398 $this->_newobj(); 6399 $this->_out('<<'.$filter.'/Length '.strlen($p).'>> '.$this->_getstream($p).' endobj'); 6400 if ($this->diskcache) { 6401 // remove temporary files 6402 unlink($this->pages[$n]); 6403 } 6404 } 6405 //Pages root 6406 $this->offsets[1] = $this->bufferlen; 6407 $out = '1 0 obj <</Type /Pages /Kids ['; 6408 foreach($this->page_obj_id as $page_obj) { 6409 $out .= ' '.$page_obj.' 0 R'; 6410 } 6411 $out .= ' ] /Count '.$nb.' >> endobj'; 6412 $this->_out($out); 6413 } 6414 6415 /** 6416 * Output references to page annotations 6417 * @param int $n page number 6418 * @access protected 6419 * @author Nicola Asuni 6420 * @since 4.7.000 (2008-08-29) 6421 */ 6422 protected function _putannotsrefs($n) { 6423 if (!(isset($this->PageAnnots[$n]) OR ($this->sign AND isset($this->signature_data['cert_type'])))) { 6424 return; 6425 } 6426 $out = '/Annots ['; 6427 if (isset($this->PageAnnots[$n])) { 6428 $num_annots = count($this->PageAnnots[$n]); 6429 for ($i = 0; $i < $num_annots; ++$i) { 6430 ++$this->curr_annot_obj_id; 6431 if (!in_array($this->curr_annot_obj_id, $this->radio_groups)) { 6432 $out .= ' '.$this->curr_annot_obj_id.' 0 R'; 6433 } else { 6434 ++$num_annots; 6435 } 6436 } 6437 } 6438 if (($n==1) AND $this->sign AND isset($this->signature_data['cert_type'])) { 6439 // set reference for signature object 6440 $out .= ' '.$this->sig_annot_ref; 6441 } 6442 $out .= ' ]'; 6443 $this->_out($out); 6444 } 6445 6446 /** 6447 * Output annotations objects for all pages. 6448 * !!! THIS METHOD IS NOT YET COMPLETED !!! 6449 * See section 12.5 of PDF 32000_2008 reference. 6450 * @access protected 6451 * @author Nicola Asuni 6452 * @since 4.0.018 (2008-08-06) 6453 */ 6454 protected function _putannotsobjs() { 6455 // reset object counter 6456 $this->annot_obj_id = $this->annots_start_obj_id; 6457 for ($n=1; $n <= $this->numpages; ++$n) { 6458 if (isset($this->PageAnnots[$n])) { 6459 // set page annotations 6460 foreach ($this->PageAnnots[$n] as $key => $pl) { 6461 // create annotation object for grouping radiobuttons 6462 if (isset($this->radiobutton_groups[$n][$pl['txt']]) AND is_array($this->radiobutton_groups[$n][$pl['txt']])) { 6463 $annots = '<<'; 6464 $annots .= ' /Type /Annot'; 6465 $annots .= ' /Subtype /Widget'; 6466 $annots .= ' /T '.$this->_dataannobjstring($pl['txt']); 6467 $annots .= ' /FT /Btn'; 6468 $annots .= ' /Ff 49152'; 6469 $annots .= ' /Kids ['; 6470 foreach ($this->radiobutton_groups[$n][$pl['txt']] as $data) { 6471 $annots .= ' '.$data['kid'].' 0 R'; 6472 if ($data['def'] !== 'Off') { 6473 $defval = $data['def']; 6474 } 6475 } 6476 $annots .= ' ]'; 6477 if (isset($defval)) { 6478 $annots .= ' /V /'.$defval; 6479 } 6480 $annots .= ' >>'; 6481 ++$this->annot_obj_id; 6482 $this->offsets[$this->annot_obj_id] = $this->bufferlen; 6483 $this->_out($this->annot_obj_id.' 0 obj '.$annots.' endobj'); 6484 $this->form_obj_id[] = $this->annot_obj_id; 6485 // store object id to be used on Parent entry of Kids 6486 $this->radiobutton_groups[$n][$pl['txt']] = $this->annot_obj_id; 6487 } 6488 $formfield = false; 6489 $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER); 6490 $a = $pl['x'] * $this->k; 6491 $b = $this->pagedim[$n]['h'] - (($pl['y'] + $pl['h']) * $this->k); 6492 $c = $pl['w'] * $this->k; 6493 $d = $pl['h'] * $this->k; 6494 $rect = sprintf('%.2F %.2F %.2F %.2F', $a, $b, $a+$c, $b+$d); 6495 // create new annotation object 6496 $annots = '<</Type /Annot'; 6497 $annots .= ' /Subtype /'.$pl['opt']['subtype']; 6498 $annots .= ' /Rect ['.$rect.']'; 6499 $ft = array('Btn', 'Tx', 'Ch', 'Sig'); 6500 if (isset($pl['opt']['ft']) AND in_array($pl['opt']['ft'], $ft)) { 6501 $annots .= ' /FT /'.$pl['opt']['ft']; 6502 $formfield = true; 6503 } 6504 $annots .= ' /Contents '.$this->_textannobjstring($pl['txt']); 6505 $annots .= ' /P '.$this->page_obj_id[$n].' 0 R'; 6506 $annots .= ' /NM '.$this->_dataannobjstring(sprintf('%04u-%04u', $n, $key)); 6507 $annots .= ' /M '.$this->_datestring(); 6508 if (isset($pl['opt']['f'])) { 6509 $val = 0; 6510 if (is_array($pl['opt']['f'])) { 6511 foreach ($pl['opt']['f'] as $f) { 6512 switch (strtolower($f)) { 6513 case 'invisible': { 6514 $val += 1 << 0; 6515 break; 6516 } 6517 case 'hidden': { 6518 $val += 1 << 1; 6519 break; 6520 } 6521 case 'print': { 6522 $val += 1 << 2; 6523 break; 6524 } 6525 case 'nozoom': { 6526 $val += 1 << 3; 6527 break; 6528 } 6529 case 'norotate': { 6530 $val += 1 << 4; 6531 break; 6532 } 6533 case 'noview': { 6534 $val += 1 << 5; 6535 break; 6536 } 6537 case 'readonly': { 6538 $val += 1 << 6; 6539 break; 6540 } 6541 case 'locked': { 6542 $val += 1 << 8; 6543 break; 6544 } 6545 case 'togglenoview': { 6546 $val += 1 << 9; 6547 break; 6548 } 6549 case 'lockedcontents': { 6550 $val += 1 << 10; 6551 break; 6552 } 6553 default: { 6554 break; 6555 } 6556 } 6557 } 6558 } else { 6559 $val = intval($pl['opt']['f']); 6560 } 6561 $annots .= ' /F '.intval($val); 6562 } 6563 if (isset($pl['opt']['as']) AND is_string($pl['opt']['as'])) { 6564 $annots .= ' /AS /'.$pl['opt']['as']; 6565 } 6566 if (isset($pl['opt']['ap'])) { 6567 // appearance stream 6568 $annots .= ' /AP <<'; 6569 if (is_array($pl['opt']['ap'])) { 6570 foreach ($pl['opt']['ap'] as $apmode => $apdef) { 6571 // $apmode can be: n = normal; r = rollover; d = down; 6572 $annots .= ' /'.strtoupper($apmode); 6573 if (is_array($apdef)) { 6574 $annots .= ' <<'; 6575 foreach ($apdef as $apstate => $stream) { 6576 // reference to XObject that define the appearance for this mode-state 6577 $apsobjid = $this->_putAPXObject($c, $d, $stream); 6578 $annots .= ' /'.$apstate.' '.$apsobjid.' 0 R'; 6579 } 6580 $annots .= ' >>'; 6581 } else { 6582 // reference to XObject that define the appearance for this mode 6583 $apsobjid = $this->_putAPXObject($c, $d, $apdef); 6584 $annots .= ' '.$apsobjid.' 0 R'; 6585 } 6586 } 6587 } else { 6588 $annots .= $pl['opt']['ap']; 6589 } 6590 $annots .= ' >>'; 6591 } 6592 if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) { 6593 $annots .= ' /BS <<'; 6594 $annots .= ' /Type /Border'; 6595 if (isset($pl['opt']['bs']['w'])) { 6596 $annots .= ' /W '.intval($pl['opt']['bs']['w']); 6597 } 6598 $bstyles = array('S', 'D', 'B', 'I', 'U'); 6599 if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $bstyles)) { 6600 $annots .= ' /S /'.$pl['opt']['bs']['s']; 6601 } 6602 if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) { 6603 $annots .= ' /D ['; 6604 foreach ($pl['opt']['bs']['d'] as $cord) { 6605 $annots .= ' '.intval($cord); 6606 } 6607 $annots .= ']'; 6608 } 6609 $annots .= ' >>'; 6610 } else { 6611 $annots .= ' /Border ['; 6612 if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) { 6613 $annots .= intval($pl['opt']['border'][0]).' '; 6614 $annots .= intval($pl['opt']['border'][1]).' '; 6615 $annots .= intval($pl['opt']['border'][2]); 6616 if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) { 6617 $annots .= ' ['; 6618 foreach ($pl['opt']['border'][3] as $dash) { 6619 $annots .= intval($dash).' '; 6620 } 6621 $annots .= ']'; 6622 } 6623 } else { 6624 $annots .= '0 0 0'; 6625 } 6626 $annots .= ']'; 6627 } 6628 if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) { 6629 $annots .= ' /BE <<'; 6630 $bstyles = array('S', 'C'); 6631 if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $markups)) { 6632 $annots .= ' /S /'.$pl['opt']['bs']['s']; 6633 } else { 6634 $annots .= ' /S /S'; 6635 } 6636 if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) { 6637 $annots .= ' /I '.sprintf(' %.4F', $pl['opt']['be']['i']); 6638 } 6639 $annots .= '>>'; 6640 } 6641 if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c'])) AND !empty($pl['opt']['c'])) { 6642 $annots .= ' /C ['; 6643 foreach ($pl['opt']['c'] as $col) { 6644 $col = intval($col); 6645 $color = $col <= 0 ? 0 : ($col >= 255 ? 1 : $col / 255); 6646 $annots .= sprintf(' %.4F', $color); 6647 } 6648 $annots .= ']'; 6649 } 6650 //$annots .= ' /StructParent '; 6651 //$annots .= ' /OC '; 6652 $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight', 'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound'); 6653 if (in_array(strtolower($pl['opt']['subtype']), $markups)) { 6654 // this is a markup type 6655 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) { 6656 $annots .= ' /T '.$this->_textannobjstring($pl['opt']['t']); 6657 } 6658 //$annots .= ' /Popup '; 6659 if (isset($pl['opt']['ca'])) { 6660 $annots .= ' /CA '.sprintf('%.4F', floatval($pl['opt']['ca'])); 6661 } 6662 if (isset($pl['opt']['rc'])) { 6663 $annots .= ' /RC '.$this->_textannobjstring($pl['opt']['rc']); 6664 } 6665 $annots .= ' /CreationDate '.$this->_datestring(); 6666 //$annots .= ' /IRT '; 6667 if (isset($pl['opt']['subj'])) { 6668 $annots .= ' /Subj '.$this->_textannobjstring($pl['opt']['subj']); 6669 } 6670 //$annots .= ' /RT '; 6671 //$annots .= ' /IT '; 6672 //$annots .= ' /ExData '; 6673 } 6674 $lineendings = array('Square', 'Circle', 'Diamond', 'OpenArrow', 'ClosedArrow', 'None', 'Butt', 'ROpenArrow', 'RClosedArrow', 'Slash'); 6675 switch (strtolower($pl['opt']['subtype'])) { 6676 case 'text': { 6677 if (isset($pl['opt']['open'])) { 6678 $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false'); 6679 } 6680 $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph'); 6681 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) { 6682 $annots .= ' /Name /'.$pl['opt']['name']; 6683 } else { 6684 $annots .= ' /Name /Note'; 6685 } 6686 $statemodels = array('Marked', 'Review'); 6687 if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) { 6688 $annots .= ' /StateModel /'.$pl['opt']['statemodel']; 6689 } else { 6690 $pl['opt']['statemodel'] = 'Marked'; 6691 $annots .= ' /StateModel /'.$pl['opt']['statemodel']; 6692 } 6693 if ($pl['opt']['statemodel'] == 'Marked') { 6694 $states = array('Accepted', 'Unmarked'); 6695 } else { 6696 $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None'); 6697 } 6698 if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) { 6699 $annots .= ' /State /'.$pl['opt']['state']; 6700 } else { 6701 if ($pl['opt']['statemodel'] == 'Marked') { 6702 $annots .= ' /State /Unmarked'; 6703 } else { 6704 $annots .= ' /State /None'; 6705 } 6706 } 6707 break; 6708 } 6709 case 'link': { 6710 if(is_string($pl['txt'])) { 6711 // external URI link 6712 $annots .= ' /A <</S /URI /URI '.$this->_dataannobjstring($this->unhtmlentities($pl['txt'])).'>>'; 6713 } else { 6714 // internal link 6715 $l = $this->links[$pl['txt']]; 6716 $annots .= sprintf(' /Dest [%d 0 R /XYZ 0 %.2F null]', (1 + (2 * $l[0])), ($this->pagedim[$l[0]]['h'] - ($l[1] * $this->k))); 6717 } 6718 $hmodes = array('N', 'I', 'O', 'P'); 6719 if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) { 6720 $annots .= ' /H /'.$pl['opt']['h']; 6721 } else { 6722 $annots .= ' /H /I'; 6723 } 6724 //$annots .= ' /PA '; 6725 //$annots .= ' /Quadpoints '; 6726 break; 6727 } 6728 case 'freetext': { 6729 if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) { 6730 $annots .= ' /DA ('.$pl['opt']['da'].')'; 6731 } 6732 if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) { 6733 $annots .= ' /Q '.intval($pl['opt']['q']); 6734 } 6735 if (isset($pl['opt']['rc'])) { 6736 $annots .= ' /RC '.$this->_textannobjstring($pl['opt']['rc']); 6737 } 6738 if (isset($pl['opt']['ds'])) { 6739 $annots .= ' /DS '.$this->_textannobjstring($pl['opt']['ds']); 6740 } 6741 if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) { 6742 $annots .= ' /CL ['; 6743 foreach ($pl['opt']['cl'] as $cl) { 6744 $annots .= sprintf('%.4F ', $cl * $this->k); 6745 } 6746 $annots .= ']'; 6747 } 6748 $tfit = array('FreeText', 'FreeTextCallout', 'FreeTextTypeWriter'); 6749 if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) { 6750 $annots .= ' /IT '.$pl['opt']['it']; 6751 } 6752 if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) { 6753 $l = $pl['opt']['rd'][0] * $this->k; 6754 $r = $pl['opt']['rd'][1] * $this->k; 6755 $t = $pl['opt']['rd'][2] * $this->k; 6756 $b = $pl['opt']['rd'][3] * $this->k; 6757 $annots .= ' /RD ['.sprintf('%.2F %.2F %.2F %.2F', $l, $r, $t, $b).']'; 6758 } 6759 if (isset($pl['opt']['le']) AND in_array($pl['opt']['le'], $lineendings)) { 6760 $annots .= ' /LE /'.$pl['opt']['le']; 6761 } 6762 break; 6763 } 6764 case 'line': { 6765 break; 6766 } 6767 case 'square': { 6768 break; 6769 } 6770 case 'circle': { 6771 break; 6772 } 6773 case 'polygon': { 6774 break; 6775 } 6776 case 'polyline': { 6777 break; 6778 } 6779 case 'highlight': { 6780 break; 6781 } 6782 case 'underline': { 6783 break; 6784 } 6785 case 'squiggly': { 6786 break; 6787 } 6788 case 'strikeout': { 6789 break; 6790 } 6791 case 'stamp': { 6792 break; 6793 } 6794 case 'caret': { 6795 break; 6796 } 6797 case 'ink': { 6798 break; 6799 } 6800 case 'popup': { 6801 break; 6802 } 6803 case 'fileattachment': { 6804 if (!isset($pl['opt']['fs'])) { 6805 break; 6806 } 6807 $filename = basename($pl['opt']['fs']); 6808 if (isset($this->embeddedfiles[$filename]['n'])) { 6809 $annots .= ' /FS <</Type /Filespec /F '.$this->_dataannobjstring($filename).' /EF <</F '.$this->embeddedfiles[$filename]['n'].' 0 R>> >>'; 6810 $iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag'); 6811 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) { 6812 $annots .= ' /Name /'.$pl['opt']['name']; 6813 } else { 6814 $annots .= ' /Name /PushPin'; 6815 } 6816 } 6817 break; 6818 } 6819 case 'sound': { 6820 if (!isset($pl['opt']['fs'])) { 6821 break; 6822 } 6823 $filename = basename($pl['opt']['fs']); 6824 if (isset($this->embeddedfiles[$filename]['n'])) { 6825 // ... TO BE COMPLETED ... 6826 // /R /C /B /E /CO /CP 6827 $annots .= ' /Sound <</Type /Filespec /F '.$this->_dataannobjstring($filename).' /EF <</F '.$this->embeddedfiles[$filename]['n'].' 0 R>> >>'; 6828 $iconsapp = array('Speaker', 'Mic'); 6829 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) { 6830 $annots .= ' /Name /'.$pl['opt']['name']; 6831 } else { 6832 $annots .= ' /Name /Speaker'; 6833 } 6834 } 6835 break; 6836 } 6837 case 'movie': { 6838 break; 6839 } 6840 case 'widget': { 6841 $hmode = array('N', 'I', 'O', 'P', 'T'); 6842 if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmode)) { 6843 $annots .= ' /H /'.$pl['opt']['h']; 6844 } 6845 if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk'])) AND !empty($pl['opt']['mk'])) { 6846 $annots .= ' /MK <<'; 6847 if (isset($pl['opt']['mk']['r'])) { 6848 $annots .= ' /R '.$pl['opt']['mk']['r']; 6849 } 6850 if (isset($pl['opt']['mk']['bc']) AND (is_array($pl['opt']['mk']['bc']))) { 6851 $annots .= ' /BC ['; 6852 foreach($pl['opt']['mk']['bc'] AS $col) { 6853 $col = intval($col); 6854 $color = $col <= 0 ? 0 : ($col >= 255 ? 1 : $col / 255); 6855 $annots .= sprintf(' %.2F', $color); 6856 } 6857 $annots .= ']'; 6858 } 6859 if (isset($pl['opt']['mk']['bg']) AND (is_array($pl['opt']['mk']['bg']))) { 6860 $annots .= ' /BG ['; 6861 foreach($pl['opt']['mk']['bg'] AS $col) { 6862 $col = intval($col); 6863 $color = $col <= 0 ? 0 : ($col >= 255 ? 1 : $col / 255); 6864 $annots .= sprintf(' %.2F', $color); 6865 } 6866 $annots .= ']'; 6867 } 6868 if (isset($pl['opt']['mk']['ca'])) { 6869 $annots .= ' /CA '.$pl['opt']['mk']['ca'].''; 6870 } 6871 if (isset($pl['opt']['mk']['rc'])) { 6872 $annots .= ' /RC '.$pl['opt']['mk']['ca'].''; 6873 } 6874 if (isset($pl['opt']['mk']['ac'])) { 6875 $annots .= ' /AC '.$pl['opt']['mk']['ca'].''; 6876 } 6877 if (isset($pl['opt']['mk']['i'])) { 6878 $info = $this->getImageBuffer($pl['opt']['mk']['i']); 6879 if ($info !== false) { 6880 $annots .= ' /I '.$info['n'].' 0 R'; 6881 } 6882 } 6883 if (isset($pl['opt']['mk']['ri'])) { 6884 $info = $this->getImageBuffer($pl['opt']['mk']['ri']); 6885 if ($info !== false) { 6886 $annots .= ' /RI '.$info['n'].' 0 R'; 6887 } 6888 } 6889 if (isset($pl['opt']['mk']['ix'])) { 6890 $info = $this->getImageBuffer($pl['opt']['mk']['ix']); 6891 if ($info !== false) { 6892 $annots .= ' /IX '.$info['n'].' 0 R'; 6893 } 6894 } 6895 if (isset($pl['opt']['mk']['if']) AND (is_array($pl['opt']['mk']['if'])) AND !empty($pl['opt']['mk']['if'])) { 6896 $annots .= ' /IF <<'; 6897 $if_sw = array('A', 'B', 'S', 'N'); 6898 if (isset($pl['opt']['mk']['if']['sw']) AND in_array($pl['opt']['mk']['if']['sw'], $if_sw)) { 6899 $annots .= ' /SW /'.$pl['opt']['mk']['if']['sw']; 6900 } 6901 $if_s = array('A', 'P'); 6902 if (isset($pl['opt']['mk']['if']['s']) AND in_array($pl['opt']['mk']['if']['s'], $if_s)) { 6903 $annots .= ' /S /'.$pl['opt']['mk']['if']['s']; 6904 } 6905 if (isset($pl['opt']['mk']['if']['a']) AND (is_array($pl['opt']['mk']['if']['a'])) AND !empty($pl['opt']['mk']['if']['a'])) { 6906 $annots .= sprintf(' /A [%.2F %.2F]', $pl['opt']['mk']['if']['a'][0], $pl['opt']['mk']['if']['a'][1]); 6907 } 6908 if (isset($pl['opt']['mk']['if']['fb']) AND ($pl['opt']['mk']['if']['fb'])) { 6909 $annots .= ' /FB true'; 6910 } 6911 $annots .= '>>'; 6912 } 6913 if (isset($pl['opt']['mk']['tp']) AND ($pl['opt']['mk']['tp'] >= 0) AND ($pl['opt']['mk']['tp'] <= 6)) { 6914 $annots .= ' /TP '.intval($pl['opt']['mk']['tp']); 6915 } else { 6916 $annots .= ' /TP 0'; 6917 } 6918 $annots .= '>>'; 6919 } // end MK 6920 // --- Entries for field dictionaries --- 6921 if (isset($this->radiobutton_groups[$n][$pl['txt']])) { 6922 // set parent 6923 $annots .= ' /Parent '.$this->radiobutton_groups[$n][$pl['txt']].' 0 R'; 6924 } 6925 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) { 6926 $annots .= ' /T '.$this->_dataannobjstring($pl['opt']['t']); 6927 } 6928 if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) { 6929 $annots .= ' /TU '.$this->_dataannobjstring($pl['opt']['tu']); 6930 } 6931 if (isset($pl['opt']['tm']) AND is_string($pl['opt']['tm'])) { 6932 $annots .= ' /TM '.$this->_dataannobjstring($pl['opt']['tm']); 6933 } 6934 if (isset($pl['opt']['ff'])) { 6935 if (is_array($pl['opt']['ff'])) { 6936 // array of bit settings 6937 $flag = 0; 6938 foreach($pl['opt']['ff'] as $val) { 6939 $flag += 1 << ($val - 1); 6940 } 6941 } else { 6942 $flag = intval($pl['opt']['ff']); 6943 } 6944 $annots .= ' /Ff '.$flag; 6945 } 6946 if (isset($pl['opt']['maxlen'])) { 6947 $annots .= ' /MaxLen '.intval($pl['opt']['maxlen']); 6948 } 6949 if (isset($pl['opt']['v'])) { 6950 $annots .= ' /V'; 6951 if (is_array($pl['opt']['v'])) { 6952 foreach ($pl['opt']['v'] AS $optval) { 6953 if (is_float($optval)) { 6954 $optval = sprintf('%.2F', $optval); 6955 } 6956 $annots .= ' '.$optval; 6957 } 6958 } else { 6959 $annots .= ' '.$this->_textannobjstring($pl['opt']['v']); 6960 } 6961 } 6962 if (isset($pl['opt']['dv'])) { 6963 $annots .= ' /DV'; 6964 if (is_array($pl['opt']['dv'])) { 6965 foreach ($pl['opt']['dv'] AS $optval) { 6966 if (is_float($optval)) { 6967 $optval = sprintf('%.2F', $optval); 6968 } 6969 $annots .= ' '.$optval; 6970 } 6971 } else { 6972 $annots .= ' '.$this->_textannobjstring($pl['opt']['dv']); 6973 } 6974 } 6975 if (isset($pl['opt']['rv'])) { 6976 $annots .= ' /RV'; 6977 if (is_array($pl['opt']['rv'])) { 6978 foreach ($pl['opt']['rv'] AS $optval) { 6979 if (is_float($optval)) { 6980 $optval = sprintf('%.2F', $optval); 6981 } 6982 $annots .= ' '.$optval; 6983 } 6984 } else { 6985 $annots .= ' '.$this->_textannobjstring($pl['opt']['rv']); 6986 } 6987 } 6988 if (isset($pl['opt']['a']) AND !empty($pl['opt']['a'])) { 6989 $annots .= ' /A << '.$pl['opt']['a'].' >>'; 6990 } 6991 if (isset($pl['opt']['aa']) AND !empty($pl['opt']['aa'])) { 6992 $annots .= ' /AA << '.$pl['opt']['aa'].' >>'; 6993 } 6994 if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) { 6995 $annots .= ' /DA ('.$pl['opt']['da'].')'; 6996 } 6997 if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) { 6998 $annots .= ' /Q '.intval($pl['opt']['q']); 6999 } 7000 if (isset($pl['opt']['opt']) AND (is_array($pl['opt']['opt'])) AND !empty($pl['opt']['opt'])) { 7001 $annots .= ' /Opt ['; 7002 foreach($pl['opt']['opt'] AS $copt) { 7003 if (is_array($copt)) { 7004 $annots .= ' ['.$this->_textannobjstring($copt[0]).' '.$this->_textannobjstring($copt[1]).']'; 7005 } else { 7006 $annots .= ' '.$this->_textannobjstring($copt); 7007 } 7008 } 7009 $annots .= ']'; 7010 } 7011 if (isset($pl['opt']['ti'])) { 7012 $annots .= ' /TI '.intval($pl['opt']['ti']); 7013 } 7014 if (isset($pl['opt']['i']) AND (is_array($pl['opt']['i'])) AND !empty($pl['opt']['i'])) { 7015 $annots .= ' /I ['; 7016 foreach($pl['opt']['i'] AS $copt) { 7017 $annots .= intval($copt).' '; 7018 } 7019 $annots .= ']'; 7020 } 7021 break; 7022 } 7023 case 'screen': { 7024 break; 7025 } 7026 case 'printermark': { 7027 break; 7028 } 7029 case 'trapnet': { 7030 break; 7031 } 7032 case 'watermark': { 7033 break; 7034 } 7035 case '3d': { 7036 break; 7037 } 7038 default: { 7039 break; 7040 } 7041 } 7042 $annots .= '>>'; 7043 // create new annotation object 7044 ++$this->annot_obj_id; 7045 $this->offsets[$this->annot_obj_id] = $this->bufferlen; 7046 $this->_out($this->annot_obj_id.' 0 obj '.$annots.' endobj'); 7047 if ($formfield AND ! isset($this->radiobutton_groups[$n][$pl['txt']])) { 7048 // store reference of form object 7049 $this->form_obj_id[] = $this->annot_obj_id; 7050 } 7051 } 7052 } 7053 } // end for each page 7054 } 7055 7056 /** 7057 * Put appearance streams XObject used to define annotation's appearance states 7058 * @param int $w annotation width 7059 * @param int $h annotation height 7060 * @param string $stream appearance stream 7061 * @return int object ID 7062 * @access protected 7063 * @since 4.8.001 (2009-09-09) 7064 */ 7065 protected function _putAPXObject($w=0, $h=0, $stream='') { 7066 $stream = trim($stream); 7067 ++$this->apxo_obj_id; 7068 $this->offsets[$this->apxo_obj_id] = $this->bufferlen; 7069 $out = $this->apxo_obj_id.' 0 obj'; 7070 $out .= ' <<'; 7071 $out .= ' /Type /XObject'; 7072 $out .= ' /Subtype /Form'; 7073 $out .= ' /FormType 1'; 7074 if ($this->compress) { 7075 $stream = gzcompress($stream); 7076 $out .= ' /Filter /FlateDecode'; 7077 } 7078 $rect = sprintf('%.2F %.2F', $w, $h); 7079 $out .= ' /BBox [0 0 '.$rect.']'; 7080 $out .= ' /Matrix [1 0 0 1 0 0]'; 7081 $out .= ' /Resources <</ProcSet [/PDF]>>'; 7082 $out .= ' /Length '.strlen($stream); 7083 $out .= ' >>'; 7084 $out .= ' '.$this->_getstream($stream); 7085 $out .= ' endobj'; 7086 $this->_out($out); 7087 return $this->apxo_obj_id; 7088 } 7089 7090 /** 7091 * Output fonts. 7092 * @access protected 7093 */ 7094 protected function _putfonts() { 7095 $nf = $this->n; 7096 foreach ($this->diffs as $diff) { 7097 //Encodings 7098 $this->_newobj(); 7099 $this->_out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.']>> endobj'); 7100 } 7101 $mqr = $this->get_mqr(); 7102 $this->set_mqr(false); 7103 foreach ($this->FontFiles as $file => $info) { 7104 // search and get font file to embedd 7105 $fontdir = $info['fontdir']; 7106 $file = strtolower($file); 7107 $fontfile = ''; 7108 // search files on various directories 7109 if (($fontdir !== false) AND file_exists($fontdir.$file)) { 7110 $fontfile = $fontdir.$file; 7111 } elseif (file_exists($this->_getfontpath().$file)) { 7112 $fontfile = $this->_getfontpath().$file; 7113 } elseif (file_exists($file)) { 7114 $fontfile = $file; 7115 } 7116 if (!$this->empty_string($fontfile)) { 7117 $font = file_get_contents($fontfile); 7118 $compressed = (substr($file, -2) == '.z'); 7119 if ((!$compressed) AND (isset($info['length2']))) { 7120 $header = (ord($font{0}) == 128); 7121 if ($header) { 7122 //Strip first binary header 7123 $font = substr($font, 6); 7124 } 7125 if ($header AND (ord($font{$info['length1']}) == 128)) { 7126 //Strip second binary header 7127 $font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] + 6)); 7128 } 7129 } 7130 $this->_newobj(); 7131 $this->FontFiles[$file]['n'] = $this->n; 7132 $out = '<</Length '.strlen($font); 7133 if ($compressed) { 7134 $out .= ' /Filter /FlateDecode'; 7135 } 7136 $out .= ' /Length1 '.$info['length1']; 7137 if (isset($info['length2'])) { 7138 $out .= ' /Length2 '.$info['length2'].' /Length3 0'; 7139 } 7140 $out .= ' >>'; 7141 $out .= ' '.$this->_getstream($font); 7142 $out .= ' endobj'; 7143 $this->_out($out); 7144 } 7145 } 7146 $this->set_mqr($mqr); 7147 foreach ($this->fontkeys as $k) { 7148 //Font objects 7149 $this->setFontSubBuffer($k, 'n', $this->n + 1); 7150 $font = $this->getFontBuffer($k); 7151 $type = $font['type']; 7152 $name = $font['name']; 7153 if ($type == 'core') { 7154 //Standard font 7155 $obj_id = $this->_newobj(); 7156 $out = '<</Type /Font'; 7157 $out .= ' /Subtype /Type1'; 7158 $out .= ' /BaseFont /'.$name; 7159 $out .= ' /Name /F'.$font['i']; 7160 if ((strtolower($name) != 'symbol') AND (strtolower($name) != 'zapfdingbats')) { 7161 $out .= ' /Encoding /WinAnsiEncoding'; 7162 } 7163 if (strtolower($name) == 'helvetica') { 7164 // add default font for annotations 7165 $this->annotation_fonts['helvetica'] = $k; 7166 } 7167 $out .= ' >> endobj'; 7168 $this->_out($out); 7169 } elseif (($type == 'Type1') OR ($type == 'TrueType')) { 7170 //Additional Type1 or TrueType font 7171 $obj_id = $this->_newobj(); 7172 $out = '<</Type /Font'; 7173 $out .= ' /Subtype /'.$type; 7174 $out .= ' /BaseFont /'.$name; 7175 $out .= ' /Name /F'.$font['i']; 7176 $out .= ' /FirstChar 32 /LastChar 255'; 7177 $out .= ' /Widths '.($this->n + 1).' 0 R'; 7178 $out .= ' /FontDescriptor '.($this->n + 2).' 0 R'; 7179 if ($font['enc']) { 7180 if (isset($font['diff'])) { 7181 $out .= ' /Encoding '.($nf + $font['diff']).' 0 R'; 7182 } else { 7183 $out .= ' /Encoding /WinAnsiEncoding'; 7184 } 7185 } 7186 $out .= ' >> endobj'; 7187 $this->_out($out); 7188 // Widths 7189 $this->_newobj(); 7190 $cw = &$font['cw']; 7191 $s = '['; 7192 for ($i = 32; $i < 256; ++$i) { 7193 $s .= $cw[$i].' '; 7194 } 7195 $this->_out($s.'] endobj'); 7196 //Descriptor 7197 $this->_newobj(); 7198 $s = '<</Type /FontDescriptor /FontName /'.$name; 7199 foreach ($font['desc'] as $fdk => $fdv) { 7200 if(is_float($fdv)) { 7201 $fdv = sprintf('%.3F', $fdv); 7202 } 7203 $s .= ' /'.$fdk.' '.$fdv.''; 7204 } 7205 if (!$this->empty_string($font['file'])) { 7206 $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R'; 7207 } 7208 $this->_out($s.'>> endobj'); 7209 } else { 7210 //Allow for additional types 7211 $mtd = '_put'.strtolower($type); 7212 if (!method_exists($this, $mtd)) { 7213 $this->Error('Unsupported font type: '.$type); 7214 } 7215 $obj_id = $this->$mtd($font); 7216 } 7217 // store object ID for current font 7218 $this->font_obj_ids[$k] = $obj_id; 7219 } 7220 } 7221 7222 /** 7223 * Outputs font widths 7224 * @parameter array $font font data 7225 * @parameter int $cidoffset offset for CID values 7226 * @return PDF command string for font widths 7227 * @author Nicola Asuni 7228 * @access protected 7229 * @since 4.4.000 (2008-12-07) 7230 */ 7231 protected function _putfontwidths($font, $cidoffset=0) { 7232 ksort($font['cw']); 7233 $rangeid = 0; 7234 $range = array(); 7235 $prevcid = -2; 7236 $prevwidth = -1; 7237 $interval = false; 7238 // for each character 7239 foreach ($font['cw'] as $cid => $width) { 7240 $cid -= $cidoffset; 7241 if ($width != $font['dw']) { 7242 if ($cid == ($prevcid + 1)) { 7243 // consecutive CID 7244 if ($width == $prevwidth) { 7245 if ($width == $range[$rangeid][0]) { 7246 $range[$rangeid][] = $width; 7247 } else { 7248 array_pop($range[$rangeid]); 7249 // new range 7250 $rangeid = $prevcid; 7251 $range[$rangeid] = array(); 7252 $range[$rangeid][] = $prevwidth; 7253 $range[$rangeid][] = $width; 7254 } 7255 $interval = true; 7256 $range[$rangeid]['interval'] = true; 7257 } else { 7258 if ($interval) { 7259 // new range 7260 $rangeid = $cid; 7261 $range[$rangeid] = array(); 7262 $range[$rangeid][] = $width; 7263 } else { 7264 $range[$rangeid][] = $width; 7265 } 7266 $interval = false; 7267 } 7268 } else { 7269 // new range 7270 $rangeid = $cid; 7271 $range[$rangeid] = array(); 7272 $range[$rangeid][] = $width; 7273 $interval = false; 7274 } 7275 $prevcid = $cid; 7276 $prevwidth = $width; 7277 } 7278 } 7279 // optimize ranges 7280 $prevk = -1; 7281 $nextk = -1; 7282 $prevint = false; 7283 foreach ($range as $k => $ws) { 7284 $cws = count($ws); 7285 if (($k == $nextk) AND (!$prevint) AND ((!isset($ws['interval'])) OR ($cws < 4))) { 7286 if (isset($range[$k]['interval'])) { 7287 unset($range[$k]['interval']); 7288 } 7289 $range[$prevk] = array_merge($range[$prevk], $range[$k]); 7290 unset($range[$k]); 7291 } else { 7292 $prevk = $k; 7293 } 7294 $nextk = $k + $cws; 7295 if (isset($ws['interval'])) { 7296 if ($cws > 3) { 7297 $prevint = true; 7298 } else { 7299 $prevint = false; 7300 } 7301 unset($range[$k]['interval']); 7302 --$nextk; 7303 } else { 7304 $prevint = false; 7305 } 7306 } 7307 // output data 7308 $w = ''; 7309 foreach ($range as $k => $ws) { 7310 if (count(array_count_values($ws)) == 1) { 7311 // interval mode is more compact 7312 $w .= ' '.$k.' '.($k + count($ws) - 1).' '.$ws[0]; 7313 } else { 7314 // range mode 7315 $w .= ' '.$k.' [ '.implode(' ', $ws).' ]'; 7316 } 7317 } 7318 return '/W ['.$w.' ]'; 7319 } 7320 7321 /** 7322 * Adds unicode fonts.<br> 7323 * Based on PDF Reference 1.3 (section 5) 7324 * @parameter array $font font data 7325 * @return int font object ID 7326 * @access protected 7327 * @author Nicola Asuni 7328 * @since 1.52.0.TC005 (2005-01-05) 7329 */ 7330 protected function _puttruetypeunicode($font) { 7331 // Type0 Font 7332 // A composite font composed of other fonts, organized hierarchically 7333 $obj_id = $this->_newobj(); 7334 $out = '<</Type /Font'; 7335 $out .= ' /Subtype /Type0'; 7336 $out .= ' /BaseFont /'.$font['name'].''; 7337 $out .= ' /Name /F'.$font['i']; 7338 $out .= ' /Encoding /'.$font['enc']; 7339 $out .= ' /ToUnicode /Identity-H'; 7340 $out .= ' /DescendantFonts ['.($this->n + 1).' 0 R]'; 7341 $out .= ' >>'; 7342 $out .= ' endobj'; 7343 $this->_out($out); 7344 // CIDFontType2 7345 // A CIDFont whose glyph descriptions are based on TrueType font technology 7346 $this->_newobj(); 7347 $out = '<</Type /Font'; 7348 $out .= ' /Subtype /CIDFontType2'; 7349 $out .= ' /BaseFont /'.$font['name']; 7350 // A dictionary containing entries that define the character collection of the CIDFont. 7351 $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry']); 7352 $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering']); 7353 $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement']; 7354 $out .= ' /CIDSystemInfo <<'.$cidinfo.'>>'; 7355 $out .= ' /FontDescriptor '.($this->n + 1).' 0 R'; 7356 $out .= ' /DW '.$font['dw']; // default width 7357 $out .= "\n".$this->_putfontwidths($font, 0); 7358 $out .= ' /CIDToGIDMap '.($this->n + 2).' 0 R >> endobj'; 7359 $this->_out($out); 7360 // Font descriptor 7361 // A font descriptor describing the CIDFont default metrics other than its glyph widths 7362 $this->_newobj(); 7363 $out = '<</Type /FontDescriptor'; 7364 $out .= ' /FontName /'.$font['name']; 7365 foreach ($font['desc'] as $key => $value) { 7366 if(is_float($value)) { 7367 $value = sprintf('%.3F', $value); 7368 } 7369 $out .= ' /'.$key.' '.$value; 7370 } 7371 $fontdir = false; 7372 if (!$this->empty_string($font['file'])) { 7373 // A stream containing a TrueType font 7374 $out .= ' /FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R'; 7375 $fontdir = $this->FontFiles[$font['file']]['fontdir']; 7376 } 7377 $out .= ' >> endobj'; 7378 $this->_out($out); 7379 if (isset($font['ctg']) AND (!$this->empty_string($font['ctg']))) { 7380 $this->_newobj(); 7381 // Embed CIDToGIDMap 7382 // A specification of the mapping from CIDs to glyph indices 7383 // search and get CTG font file to embedd 7384 $ctgfile = strtolower($font['ctg']); 7385 // search and get ctg font file to embedd 7386 $fontfile = ''; 7387 // search files on various directories 7388 if (($fontdir !== false) AND file_exists($fontdir.$ctgfile)) { 7389 $fontfile = $fontdir.$ctgfile; 7390 } elseif (file_exists($this->_getfontpath().$ctgfile)) { 7391 $fontfile = $this->_getfontpath().$ctgfile; 7392 } elseif (file_exists($ctgfile)) { 7393 $fontfile = $ctgfile; 7394 } 7395 if ($this->empty_string($fontfile)) { 7396 $this->Error('Font file not found: '.$ctgfile); 7397 } 7398 $size = filesize($fontfile); 7399 $out = '<</Length '.$size.''; 7400 if (substr($fontfile, -2) == '.z') { // check file extension 7401 // Decompresses data encoded using the public-domain 7402 // zlib/deflate compression method, reproducing the 7403 // original text or binary data 7404 $out .= ' /Filter /FlateDecode'; 7405 } 7406 $out .= ' >>'; 7407 $out .= ' '.$this->_getstream(file_get_contents($fontfile)); 7408 $out .= ' endobj'; 7409 $this->_out($out); 7410 } 7411 return $obj_id; 7412 } 7413 7414 /** 7415 * Output CID-0 fonts. 7416 * A Type 0 CIDFont contains glyph descriptions based on the Adobe Type 1 font format 7417 * @param array $font font data 7418 * @return int font object ID 7419 * @access protected 7420 * @author Andrew Whitehead, Nicola Asuni, Yukihiro Nakadaira 7421 * @since 3.2.000 (2008-06-23) 7422 */ 7423 protected function _putcidfont0($font) { 7424 $cidoffset = 0; 7425 if (!isset($font['cw'][1])) { 7426 $cidoffset = 31; 7427 } 7428 if (isset($font['cidinfo']['uni2cid'])) { 7429 // convert unicode to cid. 7430 $uni2cid = $font['cidinfo']['uni2cid']; 7431 $cw = array(); 7432 foreach ($font['cw'] as $uni => $width) { 7433 if (isset($uni2cid[$uni])) { 7434 $cw[($uni2cid[$uni] + $cidoffset)] = $width; 7435 } elseif ($uni < 256) { 7436 $cw[$uni] = $width; 7437 } // else unknown character 7438 } 7439 $font = array_merge($font, array('cw' => $cw)); 7440 } 7441 $name = $font['name']; 7442 $enc = $font['enc']; 7443 if ($enc) { 7444 $longname = $name.'-'.$enc; 7445 } else { 7446 $longname = $name; 7447 } 7448 $obj_id = $this->_newobj(); 7449 $out = '<</Type /Font'; 7450 $out .= ' /Subtype /Type0'; 7451 $out .= ' /BaseFont /'.$longname; 7452 $out .= ' /Name /F'.$font['i']; 7453 if ($enc) { 7454 $out .= ' /Encoding /'.$enc; 7455 } 7456 $out .= ' /DescendantFonts ['.($this->n + 1).' 0 R]'; 7457 $out .= ' >> endobj'; 7458 $this->_out($out); 7459 $this->_newobj(); 7460 $out = '<</Type /Font'; 7461 $out .= ' /Subtype /CIDFontType0'; 7462 $out .= ' /BaseFont /'.$name; 7463 $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry']); 7464 $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering']); 7465 $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement']; 7466 $out .= ' /CIDSystemInfo <<'.$cidinfo.'>>'; 7467 $out .= ' /FontDescriptor '.($this->n + 1).' 0 R'; 7468 $out .= ' /DW '.$font['dw']; 7469 $out .= "\n".$this->_putfontwidths($font, $cidoffset); 7470 $out .= ' >> endobj'; 7471 $this->_out($out); 7472 $this->_newobj(); 7473 $s = '<</Type /FontDescriptor /FontName /'.$name; 7474 foreach ($font['desc'] as $k => $v) { 7475 if ($k != 'Style') { 7476 if(is_float($v)) { 7477 $v = sprintf('%.3F', $v); 7478 } 7479 $s .= ' /'.$k.' '.$v.''; 7480 } 7481 } 7482 $this->_out($s.'>> endobj'); 7483 return $obj_id; 7484 } 7485 7486 /** 7487 * Output images. 7488 * @access protected 7489 */ 7490 protected function _putimages() { 7491 $filter = ($this->compress) ? '/Filter /FlateDecode ' : ''; 7492 foreach ($this->imagekeys as $file) { 7493 $info = $this->getImageBuffer($file); 7494 $this->_newobj(); 7495 $this->setImageSubBuffer($file, 'n', $this->n); 7496 $out = '<</Type /XObject'; 7497 $out .= ' /Subtype /Image'; 7498 $out .= ' /Width '.$info['w']; 7499 $out .= ' /Height '.$info['h']; 7500 if (array_key_exists('masked', $info)) { 7501 $out .= ' /SMask '.($this->n - 1).' 0 R'; 7502 } 7503 if ($info['cs'] == 'Indexed') { 7504 $out .= ' /ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n + 1).' 0 R]'; 7505 } else { 7506 $out .= ' /ColorSpace /'.$info['cs']; 7507 if ($info['cs'] == 'DeviceCMYK') { 7508 $out .= ' /Decode [1 0 1 0 1 0 1 0]'; 7509 } 7510 } 7511 $out .= ' /BitsPerComponent '.$info['bpc']; 7512 if (isset($info['f'])) { 7513 $out .= ' /Filter /'.$info['f']; 7514 } 7515 if (isset($info['parms'])) { 7516 $out .= ' '.$info['parms']; 7517 } 7518 if (isset($info['trns']) AND is_array($info['trns'])) { 7519 $trns=''; 7520 $count_info = count($info['trns']); 7521 for ($i=0; $i < $count_info; ++$i) { 7522 $trns .= $info['trns'][$i].' '.$info['trns'][$i].' '; 7523 } 7524 $out .= ' /Mask ['.$trns.']'; 7525 } 7526 $out .= ' /Length '.strlen($info['data']).' >>'; 7527 $out .= ' '.$this->_getstream($info['data']); 7528 $out .= ' endobj'; 7529 $this->_out($out); 7530 //Palette 7531 if ($info['cs'] == 'Indexed') { 7532 $this->_newobj(); 7533 $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal']; 7534 $this->_out('<<'.$filter.'/Length '.strlen($pal).'>> '.$this->_getstream($pal).' endobj'); 7535 } 7536 } 7537 } 7538 7539 /** 7540 * Output Spot Colors Resources. 7541 * @access protected 7542 * @since 4.0.024 (2008-09-12) 7543 */ 7544 protected function _putspotcolors() { 7545 foreach ($this->spot_colors as $name => $color) { 7546 $this->_newobj(); 7547 $this->spot_colors[$name]['n'] = $this->n; 7548 $out = '[/Separation /'.str_replace(' ', '#20', $name); 7549 $out .= ' /DeviceCMYK <<'; 7550 $out .= ' /Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0]'; 7551 $out .= ' '.sprintf('/C1 [%.4F %.4F %.4F %.4F] ', $color['c']/100, $color['m']/100, $color['y']/100, $color['k']/100); 7552 $out .= ' /FunctionType 2 /Domain [0 1] /N 1>>]'; 7553 $out .= ' endobj'; 7554 $this->_out($out); 7555 } 7556 } 7557 7558 /** 7559 * Output Resources Dictionary. 7560 * @access protected 7561 */ 7562 protected function _putresourcedict() { 7563 $out = '2 0 obj'; 7564 $out .= ' << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'; 7565 $out .= ' /Font <<'; 7566 foreach ($this->fontkeys as $fontkey) { 7567 $font = $this->getFontBuffer($fontkey); 7568 $out .= ' /F'.$font['i'].' '.$font['n'].' 0 R'; 7569 } 7570 $out .= ' >>'; 7571 $out .= ' /XObject <<'; 7572 foreach ($this->imagekeys as $file) { 7573 $info = $this->getImageBuffer($file); 7574 $out .= ' /I'.$info['i'].' '.$info['n'].' 0 R'; 7575 } 7576 $out .= ' >>'; 7577 // visibility 7578 $out .= ' /Properties <</OC1 '.$this->n_ocg_print.' 0 R /OC2 '.$this->n_ocg_view.' 0 R>>'; 7579 // transparency 7580 $out .= ' /ExtGState <<'; 7581 foreach ($this->extgstates as $k => $extgstate) { 7582 if (isset($extgstate['name'])) { 7583 $out .= ' /'.$extgstate['name']; 7584 } else { 7585 $out .= ' /GS'.$k; 7586 } 7587 $out .= ' '.$extgstate['n'].' 0 R'; 7588 } 7589 $out .= ' >>'; 7590 // gradient patterns 7591 if (isset($this->gradients) AND (count($this->gradients) > 0)) { 7592 $out .= ' /Pattern <<'; 7593 foreach ($this->gradients as $id => $grad) { 7594 $out .= ' /p'.$id.' '.$grad['pattern'].' 0 R'; 7595 } 7596 $out .= ' >>'; 7597 } 7598 // gradient shadings 7599 if (isset($this->gradients) AND (count($this->gradients) > 0)) { 7600 $out .= ' /Shading <<'; 7601 foreach ($this->gradients as $id => $grad) { 7602 $out .= ' /Sh'.$id.' '.$grad['id'].' 0 R'; 7603 } 7604 $out .= ' >>'; 7605 } 7606 // spot colors 7607 if (isset($this->spot_colors) AND (count($this->spot_colors) > 0)) { 7608 $out .= ' /ColorSpace <<'; 7609 foreach ($this->spot_colors as $color) { 7610 $out .= ' /CS'.$color['i'].' '.$color['n'].' 0 R'; 7611 } 7612 $out .= ' >>'; 7613 } 7614 $out .= ' >> endobj'; 7615 $this->_out($out); 7616 } 7617 7618 /** 7619 * Output Resources. 7620 * @access protected 7621 */ 7622 protected function _putresources() { 7623 $this->_putextgstates(); 7624 $this->_putocg(); 7625 $this->_putfonts(); 7626 $this->_putimages(); 7627 $this->_putspotcolors(); 7628 $this->_putshaders(); 7629 //Resource dictionary 7630 $this->offsets[2] = $this->bufferlen; 7631 $this->_putresourcedict(); 7632 $this->_putbookmarks(); 7633 $this->_putEmbeddedFiles(); 7634 $this->_putannotsobjs(); 7635 $this->_putjavascript(); 7636 // encryption 7637 $this->_putencryption(); 7638 } 7639 7640 /** 7641 * Adds some Metadata information (Document Information Dictionary) 7642 * (see Chapter 14.3.3 Document Information Dictionary of PDF32000_2008.pdf Reference) 7643 * @access protected 7644 */ 7645 protected function _putinfo() { 7646 $this->_newobj(); 7647 $out = '<<'; 7648 if (!$this->empty_string($this->title)) { 7649 // The document's title. 7650 $out .= ' /Title '.$this->_textstring($this->title); 7651 } 7652 if (!$this->empty_string($this->author)) { 7653 // The name of the person who created the document. 7654 $out .= ' /Author '.$this->_textstring($this->author); 7655 } 7656 if (!$this->empty_string($this->subject)) { 7657 // The subject of the document. 7658 $out .= ' /Subject '.$this->_textstring($this->subject); 7659 } 7660 if (!$this->empty_string($this->keywords)) { 7661 // Keywords associated with the document. 7662 $out .= ' /Keywords '.$this->_textstring($this->keywords.' TCP'.'DF'); 7663 } 7664 if (!$this->empty_string($this->creator)) { 7665 // 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. 7666 $out .= ' /Creator '.$this->_textstring($this->creator); 7667 } 7668 if (defined('PDF_PRODUCER')) { 7669 // If the document was converted to PDF from another format, the name of the conforming product that converted it to PDF. 7670 $out .= ' /Producer '.$this->_textstring(PDF_PRODUCER.' (TCP'.'DF)'); 7671 } else { 7672 // default producer 7673 $out .= ' /Producer '.$this->_textstring('TCP'.'DF'); 7674 } 7675 // The date and time the document was created, in human-readable form 7676 $out .= ' /CreationDate '.$this->_datestring(); 7677 // The date and time the document was most recently modified, in human-readable form 7678 $out .= ' /ModDate '.$this->_datestring(); 7679 // A name object indicating whether the document has been modified to include trapping information 7680 //$out .= ' /Trapped /False'); 7681 $out .= ' >> endobj'; 7682 $this->_out($out); 7683 } 7684 7685 /** 7686 * Output Catalog. 7687 * @access protected 7688 */ 7689 protected function _putcatalog() { 7690 $this->_newobj(); 7691 $out = '<< /Type /Catalog'; 7692 $out .= ' /Pages 1 0 R'; 7693 if ($this->ZoomMode == 'fullpage') { 7694 $out .= ' /OpenAction [3 0 R /Fit]'; 7695 } elseif ($this->ZoomMode == 'fullwidth') { 7696 $out .= ' /OpenAction [3 0 R /FitH null]'; 7697 } elseif ($this->ZoomMode == 'real') { 7698 $out .= ' /OpenAction [3 0 R /XYZ null null 1]'; 7699 } elseif (!is_string($this->ZoomMode)) { 7700 $out .= ' /OpenAction [3 0 R /XYZ null null '.($this->ZoomMode / 100).']'; 7701 } 7702 if (isset($this->LayoutMode) AND (!$this->empty_string($this->LayoutMode))) { 7703 $out .= ' /PageLayout /'.$this->LayoutMode; 7704 } 7705 if (isset($this->PageMode) AND (!$this->empty_string($this->PageMode))) { 7706 $out .= ' /PageMode /'.$this->PageMode; 7707 } 7708 if (isset($this->l['a_meta_language'])) { 7709 $out .= ' /Lang /'.$this->l['a_meta_language']; 7710 } 7711 $out .= ' /Names <<'; 7712 if ((!empty($this->javascript)) OR (!empty($this->js_objects))) { 7713 $out .= ' /JavaScript '.($this->n_js).' 0 R'; 7714 } 7715 $out .= ' >>'; 7716 if (count($this->outlines) > 0) { 7717 $out .= ' /Outlines '.$this->OutlineRoot.' 0 R'; 7718 $out .= ' /PageMode /UseOutlines'; 7719 } 7720 $out .= ' '.$this->_putviewerpreferences(); 7721 $p = $this->n_ocg_print.' 0 R'; 7722 $v = $this->n_ocg_view.' 0 R'; 7723 $as = '<</Event /Print /OCGs ['.$p.' '.$v.'] /Category [/Print]>> <</Event /View /OCGs ['.$p.' '.$v.'] /Category [/View]>>'; 7724 $out .= ' /OCProperties <</OCGs ['.$p.' '.$v.'] /D <</ON ['.$p.'] /OFF ['.$v.'] /AS ['.$as.']>>>>'; 7725 // AcroForm 7726 if (!empty($this->form_obj_id) OR ($this->sign AND isset($this->signature_data['cert_type']))) { 7727 $out .= ' /AcroForm<<'; 7728 $objrefs = ''; 7729 if ($this->sign AND isset($this->signature_data['cert_type'])) { 7730 $objrefs .= $this->sig_obj_id.' 0 R'; 7731 } 7732 if (!empty($this->form_obj_id)) { 7733 foreach($this->form_obj_id as $objid) { 7734 $objrefs .= ' '.$objid.' 0 R'; 7735 } 7736 } 7737 $out .= ' /Fields ['.$objrefs.']'; 7738 $out .= ' /NeedAppearances '.(empty($this->form_obj_id)?'false':'true'); 7739 if ($this->sign AND isset($this->signature_data['cert_type'])) { 7740 $out .= ' /SigFlags 3'; 7741 } 7742 //$out .= ' /CO '; 7743 if (isset($this->annotation_fonts) AND !empty($this->annotation_fonts)) { 7744 $out .= ' /DR <<'; 7745 $out .= ' /Font <<'; 7746 foreach ($this->annotation_fonts as $font => $fontkey) { 7747 $out .= ' /F'.($fontkey + 1).' '.$this->font_obj_ids[$font].' 0 R'; 7748 } 7749 $out .= ' >> >>'; 7750 } 7751 $out .= ' /DA (/F'.(array_search('helvetica', $this->fontkeys) + 1).' 0 Tf 0 g)'; 7752 $out .= ' /Q '.(($this->rtl)?'2':'0'); 7753 //$out .= ' /XFA '; 7754 $out .= ' >>'; 7755 // signatures 7756 if ($this->sign AND isset($this->signature_data['cert_type'])) { 7757 if ($this->signature_data['cert_type'] > 0) { 7758 $out .= ' /Perms<</DocMDP '.($this->sig_obj_id + 1).' 0 R>>'; 7759 } else { 7760 $out .= ' /Perms<</UR3 '.($this->sig_obj_id + 1).' 0 R>>'; 7761 } 7762 } 7763 } 7764 $out .= ' >> endobj'; 7765 $this->_out($out); 7766 } 7767 7768 /** 7769 * Output viewer preferences. 7770 * @return string for viewer preferences 7771 * @author Nicola asuni 7772 * @since 3.1.000 (2008-06-09) 7773 * @access protected 7774 */ 7775 protected function _putviewerpreferences() { 7776 $out = '/ViewerPreferences <<'; 7777 if ($this->rtl) { 7778 $out .= ' /Direction /R2L'; 7779 } else { 7780 $out .= ' /Direction /L2R'; 7781 } 7782 if (isset($this->viewer_preferences['HideToolbar']) AND ($this->viewer_preferences['HideToolbar'])) { 7783 $out .= ' /HideToolbar true'; 7784 } 7785 if (isset($this->viewer_preferences['HideMenubar']) AND ($this->viewer_preferences['HideMenubar'])) { 7786 $out .= ' /HideMenubar true'; 7787 } 7788 if (isset($this->viewer_preferences['HideWindowUI']) AND ($this->viewer_preferences['HideWindowUI'])) { 7789 $out .= ' /HideWindowUI true'; 7790 } 7791 if (isset($this->viewer_preferences['FitWindow']) AND ($this->viewer_preferences['FitWindow'])) { 7792 $out .= ' /FitWindow true'; 7793 } 7794 if (isset($this->viewer_preferences['CenterWindow']) AND ($this->viewer_preferences['CenterWindow'])) { 7795 $out .= ' /CenterWindow true'; 7796 } 7797 if (isset($this->viewer_preferences['DisplayDocTitle']) AND ($this->viewer_preferences['DisplayDocTitle'])) { 7798 $out .= ' /DisplayDocTitle true'; 7799 } 7800 if (isset($this->viewer_preferences['NonFullScreenPageMode'])) { 7801 $out .= ' /NonFullScreenPageMode /'.$this->viewer_preferences['NonFullScreenPageMode']; 7802 } 7803 if (isset($this->viewer_preferences['ViewArea'])) { 7804 $out .= ' /ViewArea /'.$this->viewer_preferences['ViewArea']; 7805 } 7806 if (isset($this->viewer_preferences['ViewClip'])) { 7807 $out .= ' /ViewClip /'.$this->viewer_preferences['ViewClip']; 7808 } 7809 if (isset($this->viewer_preferences['PrintArea'])) { 7810 $out .= ' /PrintArea /'.$this->viewer_preferences['PrintArea']; 7811 } 7812 if (isset($this->viewer_preferences['PrintClip'])) { 7813 $out .= ' /PrintClip /'.$this->viewer_preferences['PrintClip']; 7814 } 7815 if (isset($this->viewer_preferences['PrintScaling'])) { 7816 $out .= ' /PrintScaling /'.$this->viewer_preferences['PrintScaling']; 7817 } 7818 if (isset($this->viewer_preferences['Duplex']) AND (!$this->empty_string($this->viewer_preferences['Duplex']))) { 7819 $out .= ' /Duplex /'.$this->viewer_preferences['Duplex']; 7820 } 7821 if (isset($this->viewer_preferences['PickTrayByPDFSize'])) { 7822 if ($this->viewer_preferences['PickTrayByPDFSize']) { 7823 $out .= ' /PickTrayByPDFSize true'; 7824 } else { 7825 $out .= ' /PickTrayByPDFSize false'; 7826 } 7827 } 7828 if (isset($this->viewer_preferences['PrintPageRange'])) { 7829 $PrintPageRangeNum = ''; 7830 foreach ($this->viewer_preferences['PrintPageRange'] as $k => $v) { 7831 $PrintPageRangeNum .= ' '.($v - 1).''; 7832 } 7833 $out .= ' /PrintPageRange ['.substr($PrintPageRangeNum,1).']'; 7834 } 7835 if (isset($this->viewer_preferences['NumCopies'])) { 7836 $out .= ' /NumCopies '.intval($this->viewer_preferences['NumCopies']); 7837 } 7838 $out .= ' >>'; 7839 return $out; 7840 } 7841 7842 /** 7843 * Output trailer. 7844 * @access protected 7845 */ 7846 protected function _puttrailer() { 7847 $out = 'trailer <<'; 7848 $out .= ' /Size '.($this->n + 1); 7849 $out .= ' /Root '.$this->n.' 0 R'; 7850 $out .= ' /Info '.($this->n - 1).' 0 R'; 7851 if ($this->encrypted) { 7852 $out .= ' /Encrypt '.$this->enc_obj_id.' 0 R'; 7853 $out .= ' /ID [()()]'; 7854 } 7855 $out .= ' >>'; 7856 $this->_out($out); 7857 } 7858 7859 /** 7860 * Output PDF header. 7861 * @access protected 7862 */ 7863 protected function _putheader() { 7864 $this->_out('%PDF-'.$this->PDFVersion); 7865 } 7866 7867 /** 7868 * Output end of document (EOF). 7869 * @access protected 7870 */ 7871 protected function _enddoc() { 7872 $this->state = 1; 7873 $this->_putheader(); 7874 $this->_putpages(); 7875 $this->_putresources(); 7876 // Signature 7877 if ($this->sign AND isset($this->signature_data['cert_type'])) { 7878 // widget annotation for signature 7879 $this->sig_obj_id = $this->_newobj(); 7880 // --- replace signature ID on the first page --- 7881 // get the document content 7882 $pdfdoc = $this->getBuffer(); 7883 // Remove the original buffer 7884 if (isset($this->diskcache) AND $this->diskcache) { 7885 // remove buffer file from cache 7886 unlink($this->buffer); 7887 } 7888 unset($this->buffer); 7889 $signature_widget_ref = sprintf('%u 0 R', $this->sig_obj_id); 7890 $signature_widget_ref .= str_repeat(' ', (strlen($this->sig_annot_ref) - strlen($signature_widget_ref))); 7891 $pdfdoc = str_replace($this->sig_annot_ref, $signature_widget_ref, $pdfdoc); 7892 $this->diskcache = false; 7893 $this->buffer = &$pdfdoc; 7894 $this->bufferlen = strlen($pdfdoc); 7895 // --- 7896 $out = '<< /Type /Annot /Subtype /Widget /Rect [0 0 0 0]'; 7897 $out .= ' /P 3 0 R'; // link to first page object 7898 $out .= ' /FT /Sig'; 7899 $out .= ' /T '.$this->_textstring('Signature'); 7900 $out .= ' /Ff 0'; 7901 $out .= ' /V '.($this->sig_obj_id + 1).' 0 R'; 7902 $out .= ' >> endobj'; 7903 $this->_out($out); 7904 // signature 7905 $this->_putsignature(); 7906 } 7907 // Info 7908 $this->_putinfo(); 7909 // Catalog 7910 $this->_putcatalog(); 7911 // Cross-ref 7912 $o = $this->bufferlen; 7913 $this->_out('xref'); 7914 $this->_out('0 '.($this->n + 1)); 7915 $this->_out('0000000000 65535 f '); 7916 for ($i=1; $i <= $this->n; ++$i) { 7917 $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i])); 7918 } 7919 // Embedded Files 7920 if (isset($this->embeddedfiles) AND count($this->embeddedfiles) > 0) { 7921 $this->_out($this->embedded_start_obj_id.' '.count($this->embeddedfiles)); 7922 foreach ($this->embeddedfiles as $filename => $filedata) { 7923 $this->_out(sprintf('%010d 00000 n ', $this->offsets[$filedata['n']])); 7924 } 7925 } 7926 // Annotation Objects 7927 if ($this->annot_obj_id > $this->annots_start_obj_id) { 7928 $this->_out(($this->annots_start_obj_id + 1).' '.($this->annot_obj_id - $this->annots_start_obj_id)); 7929 for ($i = ($this->annots_start_obj_id + 1); $i <= $this->annot_obj_id; ++$i) { 7930 $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i])); 7931 } 7932 } 7933 // Javascript Objects 7934 if ($this->js_obj_id > $this->js_start_obj_id) { 7935 $this->_out(($this->js_start_obj_id + 1).' '.($this->js_obj_id - $this->js_start_obj_id)); 7936 for ($i = ($this->js_start_obj_id + 1); $i <= $this->js_obj_id; ++$i) { 7937 $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i])); 7938 } 7939 } 7940 // Appearance streams XObjects 7941 if ($this->apxo_obj_id > $this->apxo_start_obj_id) { 7942 $this->_out(($this->apxo_start_obj_id + 1).' '.($this->apxo_obj_id - $this->apxo_start_obj_id)); 7943 for ($i = ($this->apxo_start_obj_id + 1); $i <= $this->apxo_obj_id; ++$i) { 7944 $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i])); 7945 } 7946 } 7947 //Trailer 7948 $this->_puttrailer(); 7949 $this->_out('startxref'); 7950 $this->_out($o); 7951 $this->_out('%%EOF'); 7952 $this->state = 3; // end-of-doc 7953 if ($this->diskcache) { 7954 // remove temporary files used for images 7955 foreach ($this->imagekeys as $key) { 7956 // remove temporary files 7957 unlink($this->images[$key]); 7958 } 7959 foreach ($this->fontkeys as $key) { 7960 // remove temporary files 7961 unlink($this->fonts[$key]); 7962 } 7963 } 7964 } 7965 7966 /** 7967 * Initialize a new page. 7968 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul> 7969 * @param mixed $format The format used for pages. It can be either one of the following values (case insensitive) or a custom format in the form of a two-element array containing the width and the height (expressed in the unit given by unit).<ul><li>4A0</li><li>2A0</li><li>A0</li><li>A1</li><li>A2</li><li>A3</li><li>A4 (default)</li><li>A5</li><li>A6</li><li>A7</li><li>A8</li><li>A9</li><li>A10</li><li>B0</li><li>B1</li><li>B2</li><li>B3</li><li>B4</li><li>B5</li><li>B6</li><li>B7</li><li>B8</li><li>B9</li><li>B10</li><li>C0</li><li>C1</li><li>C2</li><li>C3</li><li>C4</li><li>C5</li><li>C6</li><li>C7</li><li>C8</li><li>C9</li><li>C10</li><li>RA0</li><li>RA1</li><li>RA2</li><li>RA3</li><li>RA4</li><li>SRA0</li><li>SRA1</li><li>SRA2</li><li>SRA3</li><li>SRA4</li><li>LETTER</li><li>LEGAL</li><li>EXECUTIVE</li><li>FOLIO</li></ul> 7970 * @access protected 7971 */ 7972 protected function _beginpage($orientation='', $format='') { 7973 ++$this->page; 7974 $this->setPageBuffer($this->page, ''); 7975 // initialize array for graphics tranformation positions inside a page buffer 7976 $this->transfmrk[$this->page] = array(); 7977 $this->state = 2; 7978 if ($this->empty_string($orientation)) { 7979 if (isset($this->CurOrientation)) { 7980 $orientation = $this->CurOrientation; 7981 } else { 7982 $orientation = 'P'; 7983 } 7984 } 7985 if ($this->empty_string($format)) { 7986 $this->setPageOrientation($orientation); 7987 } else { 7988 $this->setPageFormat($format, $orientation); 7989 } 7990 if ($this->rtl) { 7991 $this->x = $this->w - $this->rMargin; 7992 } else { 7993 $this->x = $this->lMargin; 7994 } 7995 $this->y = $this->tMargin; 7996 if (isset($this->newpagegroup[$this->page])) { 7997 // start a new group 7998 $n = sizeof($this->pagegroups) + 1; 7999 $alias = '{nb'.$n.'}'; 8000 $this->pagegroups[$alias] = 1; 8001 $this->currpagegroup = $alias; 8002 } elseif ($this->currpagegroup) { 8003 ++$this->pagegroups[$this->currpagegroup]; 8004 } 8005 } 8006 8007 /** 8008 * Mark end of page. 8009 * @access protected 8010 */ 8011 protected function _endpage() { 8012 $this->setVisibility('all'); 8013 $this->state = 1; 8014 } 8015 8016 /** 8017 * Begin a new object and return the object number. 8018 * @return int object number 8019 * @access protected 8020 */ 8021 protected function _newobj() { 8022 ++$this->n; 8023 $this->offsets[$this->n] = $this->bufferlen; 8024 $this->_out($this->n.' 0 obj'); 8025 return $this->n; 8026 } 8027 8028 /** 8029 * Underline text. 8030 * @param int $x X coordinate 8031 * @param int $y Y coordinate 8032 * @param string $txt text to underline 8033 * @access protected 8034 */ 8035 protected function _dounderline($x, $y, $txt) { 8036 $w = $this->GetStringWidth($txt); 8037 return $this->_dounderlinew($x, $y, $w); 8038 } 8039 8040 /** 8041 * Underline for rectangular text area. 8042 * @param int $x X coordinate 8043 * @param int $y Y coordinate 8044 * @param int $w width to underline 8045 * @access protected 8046 * @since 4.8.008 (2009-09-29) 8047 */ 8048 protected function _dounderlinew($x, $y, $w) { 8049 $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt; 8050 return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, ($this->h - $y + ($linew / 2)) * $this->k, $w * $this->k, $linew); 8051 } 8052 8053 /** 8054 * Line through text. 8055 * @param int $x X coordinate 8056 * @param int $y Y coordinate 8057 * @param string $txt text to linethrough 8058 * @access protected 8059 */ 8060 protected function _dolinethrough($x, $y, $txt) { 8061 $w = $this->GetStringWidth($txt); 8062 return $this->_dolinethroughw($x, $y, $w); 8063 } 8064 8065 /** 8066 * Line through for rectangular text area. 8067 * @param int $x X coordinate 8068 * @param int $y Y coordinate 8069 * @param string $txt text to linethrough 8070 * @access protected 8071 * @since 4.9.008 (2009-09-29) 8072 */ 8073 protected function _dolinethroughw($x, $y, $w) { 8074 $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt; 8075 return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, ($this->h - $y + ($this->FontSize / 3) + ($linew / 2)) * $this->k, $w * $this->k, $linew); 8076 } 8077 8078 /** 8079 * Overline text. 8080 * @param int $x X coordinate 8081 * @param int $y Y coordinate 8082 * @param string $txt text to overline 8083 * @access protected 8084 * @since 4.9.015 (2010-04-19) 8085 */ 8086 protected function _dooverline($x, $y, $txt) { 8087 $w = $this->GetStringWidth($txt); 8088 return $this->_dooverlinew($x, $y, $w); 8089 } 8090 8091 /** 8092 * Overline for rectangular text area. 8093 * @param int $x X coordinate 8094 * @param int $y Y coordinate 8095 * @param int $w width to overline 8096 * @access protected 8097 * @since 4.9.015 (2010-04-19) 8098 */ 8099 protected function _dooverlinew($x, $y, $w) { 8100 $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt; 8101 return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, ($this->h - $y + $this->FontAscent - ($linew / 2)) * $this->k, $w * $this->k, $linew); 8102 8103 } 8104 8105 /** 8106 * Read a 4-byte integer from file. 8107 * @param string $f file name. 8108 * @return 4-byte integer 8109 * @access protected 8110 */ 8111 protected function _freadint($f) { 8112 $a = unpack('Ni', fread($f, 4)); 8113 return $a['i']; 8114 } 8115 8116 /** 8117 * Add "\" before "\", "(" and ")" 8118 * @param string $s string to escape. 8119 * @return string escaped string. 8120 * @access protected 8121 */ 8122 protected function _escape($s) { 8123 // the chr(13) substitution fixes the Bugs item #1421290. 8124 return strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r')); 8125 } 8126 8127 /** 8128 * Format a data string for meta information 8129 * @param string $s data string to escape. 8130 * @return string escaped string. 8131 * @access protected 8132 */ 8133 protected function _datastring($s) { 8134 if ($this->encrypted) { 8135 $s = $this->_RC4($this->_objectkey($this->n), $s); 8136 } 8137 return '('. $this->_escape($s).')'; 8138 } 8139 8140 /** 8141 * Format a data string for annotation objects 8142 * @param string $s data string to escape. 8143 * @return string escaped string. 8144 * @access protected 8145 */ 8146 protected function _dataannobjstring($s) { 8147 if ($this->encrypted) { 8148 $s = $this->_RC4($this->_objectkey($this->annot_obj_id + 1), $s); 8149 } 8150 return '('. $this->_escape($s).')'; 8151 } 8152 8153 /** 8154 * Returns a formatted date for meta information 8155 * @return string escaped date string. 8156 * @access protected 8157 * @since 4.6.028 (2009-08-25) 8158 */ 8159 protected function _datestring() { 8160 $current_time = substr_replace(date('YmdHisO'), '\'', (0 - 2), 0).'\''; 8161 return $this->_datastring('D:'.$current_time); 8162 } 8163 8164 /** 8165 * Format a text string for meta information 8166 * @param string $s string to escape. 8167 * @return string escaped string. 8168 * @access protected 8169 */ 8170 protected function _textstring($s) { 8171 if ($this->isunicode) { 8172 //Convert string to UTF-16BE 8173 $s = $this->UTF8ToUTF16BE($s, true); 8174 } 8175 return $this->_datastring($s); 8176 } 8177 8178 /** 8179 * Format a UTF-8 text string for meta information on annotations 8180 * @param string $s string to escape. 8181 * @return string escaped string. 8182 * @access protected 8183 */ 8184 protected function _textannobjstring($s) { 8185 if ($this->isunicode) { 8186 //Convert string to UTF-16BE 8187 $s = $this->UTF8ToUTF16BE($s, true); 8188 } 8189 return $this->_dataannobjstring($s); 8190 } 8191 8192 /** 8193 * Format a text string 8194 * @param string $s string to escape. 8195 * @return string escaped string. 8196 * @access protected 8197 */ 8198 protected function _escapetext($s) { 8199 if ($this->isunicode) { 8200 if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) { 8201 $s = $this->UTF8ToLatin1($s); 8202 } else { 8203 //Convert string to UTF-16BE and reverse RTL language 8204 $s = $this->utf8StrRev($s, false, $this->tmprtl); 8205 } 8206 } 8207 return $this->_escape($s); 8208 } 8209 8210 /** 8211 * Format output stream. 8212 * @param string $s string to output. 8213 * @param int $n object reference for encryption mode 8214 * @access protected 8215 */ 8216 protected function _getstream($s, $n=0) { 8217 if ($this->encrypted) { 8218 if ($n <= 0) { 8219 // default to current object 8220 $n = $this->n; 8221 } 8222 $s = $this->_RC4($this->_objectkey($n), $s); 8223 } 8224 return "stream\n".$s."\nendstream"; 8225 } 8226 8227 /** 8228 * Output a stream. 8229 * @param string $s string to output. 8230 * @param int $n object reference for encryption mode 8231 * @access protected 8232 */ 8233 protected function _putstream($s, $n=0) { 8234 $this->_out($this->_getstream($s, $n)); 8235 } 8236 8237 /** 8238 * Output a string to the document. 8239 * @param string $s string to output. 8240 * @access protected 8241 */ 8242 protected function _out($s) { 8243 if ($this->state == 2) { 8244 if ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) { 8245 // puts data before page footer 8246 $pagebuff = $this->getPageBuffer($this->page); 8247 $page = substr($pagebuff, 0, -$this->footerlen[$this->page]); 8248 $footer = substr($pagebuff, -$this->footerlen[$this->page]); 8249 $this->setPageBuffer($this->page, $page.$s."\n".$footer); 8250 // update footer position 8251 $this->footerpos[$this->page] += strlen($s."\n"); 8252 } else { 8253 $this->setPageBuffer($this->page, $s."\n", true); 8254 } 8255 } else { 8256 $this->setBuffer($s."\n"); 8257 } 8258 } 8259 8260 /** 8261 * Converts UTF-8 strings to codepoints array.<br> 8262 * Invalid byte sequences will be replaced with 0xFFFD (replacement character)<br> 8263 * Based on: http://www.faqs.org/rfcs/rfc3629.html 8264 * <pre> 8265 * Char. number range | UTF-8 octet sequence 8266 * (hexadecimal) | (binary) 8267 * --------------------+----------------------------------------------- 8268 * 0000 0000-0000 007F | 0xxxxxxx 8269 * 0000 0080-0000 07FF | 110xxxxx 10xxxxxx 8270 * 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx 8271 * 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 8272 * --------------------------------------------------------------------- 8273 * 8274 * ABFN notation: 8275 * --------------------------------------------------------------------- 8276 * UTF8-octets = *( UTF8-char ) 8277 * UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4 8278 * UTF8-1 = %x00-7F 8279 * UTF8-2 = %xC2-DF UTF8-tail 8280 * 8281 * UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) / 8282 * %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail ) 8283 * UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) / 8284 * %xF4 %x80-8F 2( UTF8-tail ) 8285 * UTF8-tail = %x80-BF 8286 * --------------------------------------------------------------------- 8287 * </pre> 8288 * @param string $str string to process. 8289 * @return array containing codepoints (UTF-8 characters values) 8290 * @access protected 8291 * @author Nicola Asuni 8292 * @since 1.53.0.TC005 (2005-01-05) 8293 */ 8294 protected function UTF8StringToArray($str) { 8295 if (isset($this->cache_UTF8StringToArray['_'.$str])) { 8296 // return cached value 8297 return($this->cache_UTF8StringToArray['_'.$str]); 8298 } 8299 // check cache size 8300 if ($this->cache_size_UTF8StringToArray >= $this->cache_maxsize_UTF8StringToArray) { 8301 // remove first element 8302 array_shift($this->cache_UTF8StringToArray); 8303 } 8304 ++$this->cache_size_UTF8StringToArray; 8305 if (!$this->isunicode) { 8306 // split string into array of equivalent codes 8307 $strarr = array(); 8308 $strlen = strlen($str); 8309 for ($i=0; $i < $strlen; ++$i) { 8310 $strarr[] = ord($str{$i}); 8311 } 8312 // insert new value on cache 8313 $this->cache_UTF8StringToArray['_'.$str] = $strarr; 8314 return $strarr; 8315 } 8316 $unicode = array(); // array containing unicode values 8317 $bytes = array(); // array containing single character byte sequences 8318 $numbytes = 1; // number of octetc needed to represent the UTF-8 character 8319 $str .= ''; // force $str to be a string 8320 $length = strlen($str); 8321 for ($i = 0; $i < $length; ++$i) { 8322 $char = ord($str{$i}); // get one string character at time 8323 if (count($bytes) == 0) { // get starting octect 8324 if ($char <= 0x7F) { 8325 $unicode[] = $char; // use the character "as is" because is ASCII 8326 $numbytes = 1; 8327 } elseif (($char >> 0x05) == 0x06) { // 2 bytes character (0x06 = 110 BIN) 8328 $bytes[] = ($char - 0xC0) << 0x06; 8329 $numbytes = 2; 8330 } elseif (($char >> 0x04) == 0x0E) { // 3 bytes character (0x0E = 1110 BIN) 8331 $bytes[] = ($char - 0xE0) << 0x0C; 8332 $numbytes = 3; 8333 } elseif (($char >> 0x03) == 0x1E) { // 4 bytes character (0x1E = 11110 BIN) 8334 $bytes[] = ($char - 0xF0) << 0x12; 8335 $numbytes = 4; 8336 } else { 8337 // use replacement character for other invalid sequences 8338 $unicode[] = 0xFFFD; 8339 $bytes = array(); 8340 $numbytes = 1; 8341 } 8342 } elseif (($char >> 0x06) == 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN 8343 $bytes[] = $char - 0x80; 8344 if (count($bytes) == $numbytes) { 8345 // compose UTF-8 bytes to a single unicode value 8346 $char = $bytes[0]; 8347 for ($j = 1; $j < $numbytes; ++$j) { 8348 $char += ($bytes[$j] << (($numbytes - $j - 1) * 0x06)); 8349 } 8350 if ((($char >= 0xD800) AND ($char <= 0xDFFF)) OR ($char >= 0x10FFFF)) { 8351 /* The definition of UTF-8 prohibits encoding character numbers between 8352 U+D800 and U+DFFF, which are reserved for use with the UTF-16 8353 encoding form (as surrogate pairs) and do not directly represent 8354 characters. */ 8355 $unicode[] = 0xFFFD; // use replacement character 8356 } else { 8357 $unicode[] = $char; // add char to array 8358 } 8359 // reset data for next char 8360 $bytes = array(); 8361 $numbytes = 1; 8362 } 8363 } else { 8364 // use replacement character for other invalid sequences 8365 $unicode[] = 0xFFFD; 8366 $bytes = array(); 8367 $numbytes = 1; 8368 } 8369 } 8370 // insert new value on cache 8371 $this->cache_UTF8StringToArray['_'.$str] = $unicode; 8372 return $unicode; 8373 } 8374 8375 /** 8376 * Converts UTF-8 strings to UTF16-BE.<br> 8377 * @param string $str string to process. 8378 * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF) 8379 * @return string 8380 * @access protected 8381 * @author Nicola Asuni 8382 * @since 1.53.0.TC005 (2005-01-05) 8383 * @uses UTF8StringToArray(), arrUTF8ToUTF16BE() 8384 */ 8385 protected function UTF8ToUTF16BE($str, $setbom=true) { 8386 if (!$this->isunicode) { 8387 return $str; // string is not in unicode 8388 } 8389 $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values 8390 return $this->arrUTF8ToUTF16BE($unicode, $setbom); 8391 } 8392 8393 /** 8394 * Converts UTF-8 strings to Latin1 when using the standard 14 core fonts.<br> 8395 * @param string $str string to process. 8396 * @return string 8397 * @author Andrew Whitehead, Nicola Asuni 8398 * @access protected 8399 * @since 3.2.000 (2008-06-23) 8400 */ 8401 protected function UTF8ToLatin1($str) { 8402 global $utf8tolatin; 8403 if (!$this->isunicode) { 8404 return $str; // string is not in unicode 8405 } 8406 $outstr = ''; // string to be returned 8407 $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values 8408 foreach ($unicode as $char) { 8409 if ($char < 256) { 8410 $outstr .= chr($char); 8411 } elseif (array_key_exists($char, $utf8tolatin)) { 8412 // map from UTF-8 8413 $outstr .= chr($utf8tolatin[$char]); 8414 } elseif ($char == 0xFFFD) { 8415 // skip 8416 } else { 8417 $outstr .= '?'; 8418 } 8419 } 8420 return $outstr; 8421 } 8422 8423 /** 8424 * Converts UTF-8 characters array to array of Latin1 characters<br> 8425 * @param array $unicode array containing UTF-8 unicode values 8426 * @return array 8427 * @author Nicola Asuni 8428 * @access protected 8429 * @since 4.8.023 (2010-01-15) 8430 */ 8431 protected function UTF8ArrToLatin1($unicode) { 8432 global $utf8tolatin; 8433 if ((!$this->isunicode) OR ($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) { 8434 return $unicode; // string is not in unicode 8435 } 8436 $outarr = array(); // array to be returned 8437 foreach ($unicode as $char) { 8438 if ($char < 256) { 8439 $outarr[] = $char; 8440 } elseif (array_key_exists($char, $utf8tolatin)) { 8441 // map from UTF-8 8442 $outarr[] = $utf8tolatin[$char]; 8443 } elseif ($char == 0xFFFD) { 8444 // skip 8445 } else { 8446 $outarr[] = 63; // '?' character 8447 } 8448 } 8449 return $outarr; 8450 } 8451 8452 /** 8453 * Converts array of UTF-8 characters to UTF16-BE string.<br> 8454 * Based on: http://www.faqs.org/rfcs/rfc2781.html 8455 * <pre> 8456 * Encoding UTF-16: 8457 * 8458 * Encoding of a single character from an ISO 10646 character value to 8459 * UTF-16 proceeds as follows. Let U be the character number, no greater 8460 * than 0x10FFFF. 8461 * 8462 * 1) If U < 0x10000, encode U as a 16-bit unsigned integer and 8463 * terminate. 8464 * 8465 * 2) Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF, 8466 * U' must be less than or equal to 0xFFFFF. That is, U' can be 8467 * represented in 20 bits. 8468 * 8469 * 3) Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and 8470 * 0xDC00, respectively. These integers each have 10 bits free to 8471 * encode the character value, for a total of 20 bits. 8472 * 8473 * 4) Assign the 10 high-order bits of the 20-bit U' to the 10 low-order 8474 * bits of W1 and the 10 low-order bits of U' to the 10 low-order 8475 * bits of W2. Terminate. 8476 * 8477 * Graphically, steps 2 through 4 look like: 8478 * U' = yyyyyyyyyyxxxxxxxxxx 8479 * W1 = 110110yyyyyyyyyy 8480 * W2 = 110111xxxxxxxxxx 8481 * </pre> 8482 * @param array $unicode array containing UTF-8 unicode values 8483 * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF) 8484 * @return string 8485 * @access protected 8486 * @author Nicola Asuni 8487 * @since 2.1.000 (2008-01-08) 8488 * @see UTF8ToUTF16BE() 8489 */ 8490 protected function arrUTF8ToUTF16BE($unicode, $setbom=true) { 8491 $outstr = ''; // string to be returned 8492 if ($setbom) { 8493 $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM) 8494 } 8495 foreach ($unicode as $char) { 8496 if ($char == 0xFFFD) { 8497 $outstr .= "\xFF\xFD"; // replacement character 8498 } elseif ($char < 0x10000) { 8499 $outstr .= chr($char >> 0x08); 8500 $outstr .= chr($char & 0xFF); 8501 } else { 8502 $char -= 0x10000; 8503 $w1 = 0xD800 | ($char >> 0x10); 8504 $w2 = 0xDC00 | ($char & 0x3FF); 8505 $outstr .= chr($w1 >> 0x08); 8506 $outstr .= chr($w1 & 0xFF); 8507 $outstr .= chr($w2 >> 0x08); 8508 $outstr .= chr($w2 & 0xFF); 8509 } 8510 } 8511 return $outstr; 8512 } 8513 // ==================================================== 8514 8515 /** 8516 * Set header font. 8517 * @param array $font font 8518 * @access public 8519 * @since 1.1 8520 */ 8521 public function setHeaderFont($font) { 8522 $this->header_font = $font; 8523 } 8524 8525 /** 8526 * Get header font. 8527 * @return array() 8528 * @access public 8529 * @since 4.0.012 (2008-07-24) 8530 */ 8531 public function getHeaderFont() { 8532 return $this->header_font; 8533 } 8534 8535 /** 8536 * Set footer font. 8537 * @param array $font font 8538 * @access public 8539 * @since 1.1 8540 */ 8541 public function setFooterFont($font) { 8542 $this->footer_font = $font; 8543 } 8544 8545 /** 8546 * Get Footer font. 8547 * @return array() 8548 * @access public 8549 * @since 4.0.012 (2008-07-24) 8550 */ 8551 public function getFooterFont() { 8552 return $this->footer_font; 8553 } 8554 8555 /** 8556 * Set language array. 8557 * @param array $language 8558 * @access public 8559 * @since 1.1 8560 */ 8561 public function setLanguageArray($language) { 8562 $this->l = $language; 8563 if (isset($this->l['a_meta_dir'])) { 8564 $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false; 8565 } else { 8566 $this->rtl = false; 8567 } 8568 } 8569 8570 /** 8571 * Returns the PDF data. 8572 * @access public 8573 */ 8574 public function getPDFData() { 8575 if ($this->state < 3) { 8576 $this->Close(); 8577 } 8578 return $this->buffer; 8579 } 8580 8581 /** 8582 * Output anchor link. 8583 * @param string $url link URL or internal link (i.e.: <a href="#23">link to page 23</a>) 8584 * @param string $name link name 8585 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. 8586 * @param boolean $firstline if true prints only the first line and return the remaining string. 8587 * @param array $color array of RGB text color 8588 * @param string $style font style (U, D, B, I) 8589 * @param boolean $firstblock if true the string is the starting of a line. 8590 * @return the number of cells used or the remaining text if $firstline = true; 8591 * @access public 8592 */ 8593 public function addHtmlLink($url, $name, $fill=0, $firstline=false, $color='', $style=-1, $firstblock=false) { 8594 if (!$this->empty_string($url) AND ($url{0} == '#')) { 8595 // convert url to internal link 8596 $page = intval(substr($url, 1)); 8597 $url = $this->AddLink(); 8598 $this->SetLink($url, 0, $page); 8599 } 8600 // store current settings 8601 $prevcolor = $this->fgcolor; 8602 $prevstyle = $this->FontStyle; 8603 if (empty($color)) { 8604 $this->SetTextColorArray($this->htmlLinkColorArray); 8605 } else { 8606 $this->SetTextColorArray($color); 8607 } 8608 if ($style == -1) { 8609 $this->SetFont('', $this->FontStyle.$this->htmlLinkFontStyle); 8610 } else { 8611 $this->SetFont('', $this->FontStyle.$style); 8612 } 8613 $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline, $firstblock, 0); 8614 // restore settings 8615 $this->SetFont('', $prevstyle); 8616 $this->SetTextColorArray($prevcolor); 8617 return $ret; 8618 } 8619 8620 /** 8621 * Returns an associative array (keys: R,G,B) from an html color name or a six-digit or three-digit hexadecimal color representation (i.e. #3FE5AA or #7FF). 8622 * @param string $color html color 8623 * @return array RGB color or false in case of error. 8624 * @access public 8625 */ 8626 public function convertHTMLColorToDec($color='#FFFFFF') { 8627 global $webcolor; 8628 $returncolor = false; 8629 $color = preg_replace('/[\s]*/', '', $color); // remove extra spaces 8630 $color = strtolower($color); 8631 if (($dotpos = strpos($color, '.')) !== false) { 8632 // remove class parent (i.e.: color.red) 8633 $color = substr($color, ($dotpos + 1)); 8634 } 8635 if (strlen($color) == 0) { 8636 return false; 8637 } 8638 if (substr($color, 0, 3) == 'rgb') { 8639 $codes = substr($color, 4); 8640 $codes = str_replace(')', '', $codes); 8641 $returncolor = explode(',', $codes, 3); 8642 return $returncolor; 8643 } 8644 if (substr($color, 0, 1) != '#') { 8645 // decode color name 8646 if (isset($webcolor[$color])) { 8647 $color_code = $webcolor[$color]; 8648 } else { 8649 return false; 8650 } 8651 } else { 8652 $color_code = substr($color, 1); 8653 } 8654 switch (strlen($color_code)) { 8655 case 3: { 8656 // three-digit hexadecimal representation 8657 $r = substr($color_code, 0, 1); 8658 $g = substr($color_code, 1, 1); 8659 $b = substr($color_code, 2, 1); 8660 $returncolor['R'] = hexdec($r.$r); 8661 $returncolor['G'] = hexdec($g.$g); 8662 $returncolor['B'] = hexdec($b.$b); 8663 break; 8664 } 8665 case 6: { 8666 // six-digit hexadecimal representation 8667 $returncolor['R'] = hexdec(substr($color_code, 0, 2)); 8668 $returncolor['G'] = hexdec(substr($color_code, 2, 2)); 8669 $returncolor['B'] = hexdec(substr($color_code, 4, 2)); 8670 break; 8671 } 8672 } 8673 return $returncolor; 8674 } 8675 8676 /** 8677 * Converts pixels to User's Units. 8678 * @param int $px pixels 8679 * @return float value in user's unit 8680 * @access public 8681 * @see setImageScale(), getImageScale() 8682 */ 8683 public function pixelsToUnits($px) { 8684 return ($px / ($this->imgscale * $this->k)); 8685 } 8686 8687 /** 8688 * Reverse function for htmlentities. 8689 * Convert entities in UTF-8. 8690 * @param $text_to_convert Text to convert. 8691 * @return string converted 8692 * @access public 8693 */ 8694 public function unhtmlentities($text_to_convert) { 8695 return html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding); 8696 } 8697 8698 // ENCRYPTION METHODS ---------------------------------- 8699 // SINCE 2.0.000 (2008-01-02) 8700 8701 /** 8702 * Compute encryption key depending on object number where the encrypted data is stored 8703 * @param int $n object number 8704 * @access protected 8705 * @since 2.0.000 (2008-01-02) 8706 */ 8707 protected function _objectkey($n) { 8708 return substr($this->_md5_16($this->encryption_key.pack('VXxx', $n)), 0, 10); 8709 } 8710 8711 /** 8712 * Put encryption on PDF document. 8713 * @access protected 8714 * @since 2.0.000 (2008-01-02) 8715 */ 8716 protected function _putencryption() { 8717 if (!$this->encrypted) { 8718 return; 8719 } 8720 $this->_newobj(); 8721 $this->enc_obj_id = $this->n; 8722 $out = '<< /Filter /Standard /V 1 /R 2'; 8723 $out .= ' /O ('.$this->_escape($this->Ovalue).')'; 8724 $out .= ' /U ('.$this->_escape($this->Uvalue).')'; 8725 $out .= ' /P '.$this->Pvalue; 8726 $out .= ' >> endobj'; 8727 $this->_out($out); 8728 } 8729 8730 /** 8731 * Returns the input text exrypted using RC4 algorithm and the specified key. 8732 * RC4 is the standard encryption algorithm used in PDF format 8733 * @param string $key encryption key 8734 * @param String $text input text to be encrypted 8735 * @return String encrypted text 8736 * @access protected 8737 * @since 2.0.000 (2008-01-02) 8738 * @author Klemen Vodopivec 8739 */ 8740 protected function _RC4($key, $text) { 8741 if ($this->last_rc4_key != $key) { 8742 $k = str_repeat($key, ((256 / strlen($key)) + 1)); 8743 $rc4 = range(0, 255); 8744 $j = 0; 8745 for ($i = 0; $i < 256; ++$i) { 8746 $t = $rc4[$i]; 8747 $j = ($j + $t + ord($k{$i})) % 256; 8748 $rc4[$i] = $rc4[$j]; 8749 $rc4[$j] = $t; 8750 } 8751 $this->last_rc4_key = $key; 8752 $this->last_rc4_key_c = $rc4; 8753 } else { 8754 $rc4 = $this->last_rc4_key_c; 8755 } 8756 $len = strlen($text); 8757 $a = 0; 8758 $b = 0; 8759 $out = ''; 8760 for ($i = 0; $i < $len; ++$i) { 8761 $a = ($a + 1) % 256; 8762 $t = $rc4[$a]; 8763 $b = ($b + $t) % 256; 8764 $rc4[$a] = $rc4[$b]; 8765 $rc4[$b] = $t; 8766 $k = $rc4[($rc4[$a] + $rc4[$b]) % 256]; 8767 $out .= chr(ord($text{$i}) ^ $k); 8768 } 8769 return $out; 8770 } 8771 8772 /** 8773 * Encrypts a string using MD5 and returns it's value as a binary string. 8774 * @param string $str input string 8775 * @return String MD5 encrypted binary string 8776 * @access protected 8777 * @since 2.0.000 (2008-01-02) 8778 * @author Klemen Vodopivec 8779 */ 8780 protected function _md5_16($str) { 8781 return pack('H*', md5($str)); 8782 } 8783 8784 /** 8785 * Compute O value (used for RC4 encryption) 8786 * @param String $user_pass user password 8787 * @param String $owner_pass user password 8788 * @return String O value 8789 * @access protected 8790 * @since 2.0.000 (2008-01-02) 8791 * @author Klemen Vodopivec 8792 */ 8793 protected function _Ovalue($user_pass, $owner_pass) { 8794 $tmp = $this->_md5_16($owner_pass); 8795 $owner_RC4_key = substr($tmp, 0, 5); 8796 return $this->_RC4($owner_RC4_key, $user_pass); 8797 } 8798 8799 /** 8800 * Compute U value (used for RC4 encryption) 8801 * @return String U value 8802 * @access protected 8803 * @since 2.0.000 (2008-01-02) 8804 * @author Klemen Vodopivec 8805 */ 8806 protected function _Uvalue() { 8807 return $this->_RC4($this->encryption_key, $this->padding); 8808 } 8809 8810 /** 8811 * Compute encryption key 8812 * @param String $user_pass user password 8813 * @param String $owner_pass user password 8814 * @param String $protection protection type 8815 * @access protected 8816 * @since 2.0.000 (2008-01-02) 8817 * @author Klemen Vodopivec 8818 */ 8819 protected function _generateencryptionkey($user_pass, $owner_pass, $protection) { 8820 // Pad passwords 8821 $user_pass = substr($user_pass.$this->padding, 0, 32); 8822 $owner_pass = substr($owner_pass.$this->padding, 0, 32); 8823 // Compute O value 8824 $this->Ovalue = $this->_Ovalue($user_pass, $owner_pass); 8825 // Compute encyption key 8826 $tmp = $this->_md5_16($user_pass.$this->Ovalue.chr($protection)."\xFF\xFF\xFF"); 8827 $this->encryption_key = substr($tmp, 0, 5); 8828 // Compute U value 8829 $this->Uvalue = $this->_Uvalue(); 8830 // Compute P value 8831 $this->Pvalue = -(($protection^255) + 1); 8832 } 8833 8834 /** 8835 * Set document protection 8836 * The permission array is composed of values taken from the following ones: 8837 * - copy: copy text and images to the clipboard 8838 * - print: print the document 8839 * - modify: modify it (except for annotations and forms) 8840 * - annot-forms: add annotations and forms 8841 * Remark: the protection against modification is for people who have the full Acrobat product. 8842 * If you don't set any password, the document will open as usual. If you set a user password, the PDF viewer will ask for it before displaying the document. The master password, if different from the user one, can be used to get full access. 8843 * Note: protecting a document requires to encrypt it, which increases the processing time a lot. This can cause a PHP time-out in some cases, especially if the document contains images or fonts. 8844 * @param Array $permissions the set of permissions. Empty by default (only viewing is allowed). (print, modify, copy, annot-forms) 8845 * @param String $user_pass user password. Empty by default. 8846 * @param String $owner_pass owner password. If not specified, a random value is used. 8847 * @access public 8848 * @since 2.0.000 (2008-01-02) 8849 * @author Klemen Vodopivec 8850 */ 8851 public function SetProtection($permissions=array(), $user_pass='', $owner_pass=null) { 8852 $options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'annot-forms' => 32); 8853 $protection = 192; 8854 foreach ($permissions as $permission) { 8855 if (!isset($options[$permission])) { 8856 $this->Error('Incorrect permission: '.$permission); 8857 } 8858 $protection += $options[$permission]; 8859 } 8860 if ($owner_pass === null) { 8861 $owner_pass = uniqid(rand()); 8862 } 8863 $this->encrypted = true; 8864 $this->_generateencryptionkey($user_pass, $owner_pass, $protection); 8865 } 8866 8867 // END OF ENCRYPTION FUNCTIONS ------------------------- 8868 8869 // START TRANSFORMATIONS SECTION ----------------------- 8870 8871 /** 8872 * Starts a 2D tranformation saving current graphic state. 8873 * This function must be called before scaling, mirroring, translation, rotation and skewing. 8874 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior. 8875 * @access public 8876 * @since 2.1.000 (2008-01-07) 8877 * @see StartTransform(), StopTransform() 8878 */ 8879 public function StartTransform() { 8880 $this->_out('q'); 8881 $this->transfmrk[$this->page][] = $this->pagelen[$this->page]; 8882 ++$this->transfmatrix_key; 8883 $this->transfmatrix[$this->transfmatrix_key] = array(); 8884 } 8885 8886 /** 8887 * Stops a 2D tranformation restoring previous graphic state. 8888 * This function must be called after scaling, mirroring, translation, rotation and skewing. 8889 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior. 8890 * @access public 8891 * @since 2.1.000 (2008-01-07) 8892 * @see StartTransform(), StopTransform() 8893 */ 8894 public function StopTransform() { 8895 $this->_out('Q'); 8896 if (isset($this->transfmatrix[$this->transfmatrix_key])) { 8897 array_pop($this->transfmatrix[$this->transfmatrix_key]); 8898 --$this->transfmatrix_key; 8899 } 8900 array_pop($this->transfmrk[$this->page]); 8901 } 8902 /** 8903 * Horizontal Scaling. 8904 * @param float $s_x scaling factor for width as percent. 0 is not allowed. 8905 * @param int $x abscissa of the scaling center. Default is current x position 8906 * @param int $y ordinate of the scaling center. Default is current y position 8907 * @access public 8908 * @since 2.1.000 (2008-01-07) 8909 * @see StartTransform(), StopTransform() 8910 */ 8911 public function ScaleX($s_x, $x='', $y='') { 8912 $this->Scale($s_x, 100, $x, $y); 8913 } 8914 8915 /** 8916 * Vertical Scaling. 8917 * @param float $s_y scaling factor for height as percent. 0 is not allowed. 8918 * @param int $x abscissa of the scaling center. Default is current x position 8919 * @param int $y ordinate of the scaling center. Default is current y position 8920 * @access public 8921 * @since 2.1.000 (2008-01-07) 8922 * @see StartTransform(), StopTransform() 8923 */ 8924 public function ScaleY($s_y, $x='', $y='') { 8925 $this->Scale(100, $s_y, $x, $y); 8926 } 8927 8928 /** 8929 * Vertical and horizontal proportional Scaling. 8930 * @param float $s scaling factor for width and height as percent. 0 is not allowed. 8931 * @param int $x abscissa of the scaling center. Default is current x position 8932 * @param int $y ordinate of the scaling center. Default is current y position 8933 * @access public 8934 * @since 2.1.000 (2008-01-07) 8935 * @see StartTransform(), StopTransform() 8936 */ 8937 public function ScaleXY($s, $x='', $y='') { 8938 $this->Scale($s, $s, $x, $y); 8939 } 8940 8941 /** 8942 * Vertical and horizontal non-proportional Scaling. 8943 * @param float $s_x scaling factor for width as percent. 0 is not allowed. 8944 * @param float $s_y scaling factor for height as percent. 0 is not allowed. 8945 * @param int $x abscissa of the scaling center. Default is current x position 8946 * @param int $y ordinate of the scaling center. Default is current y position 8947 * @access public 8948 * @since 2.1.000 (2008-01-07) 8949 * @see StartTransform(), StopTransform() 8950 */ 8951 public function Scale($s_x, $s_y, $x='', $y='') { 8952 if ($x === '') { 8953 $x = $this->x; 8954 } 8955 if ($y === '') { 8956 $y = $this->y; 8957 } 8958 if (($s_x == 0) OR ($s_y == 0)) { 8959 $this->Error('Please do not use values equal to zero for scaling'); 8960 } 8961 $y = ($this->h - $y) * $this->k; 8962 $x *= $this->k; 8963 //calculate elements of transformation matrix 8964 $s_x /= 100; 8965 $s_y /= 100; 8966 $tm[0] = $s_x; 8967 $tm[1] = 0; 8968 $tm[2] = 0; 8969 $tm[3] = $s_y; 8970 $tm[4] = $x * (1 - $s_x); 8971 $tm[5] = $y * (1 - $s_y); 8972 //scale the coordinate system 8973 $this->Transform($tm); 8974 } 8975 8976 /** 8977 * Horizontal Mirroring. 8978 * @param int $x abscissa of the point. Default is current x position 8979 * @access public 8980 * @since 2.1.000 (2008-01-07) 8981 * @see StartTransform(), StopTransform() 8982 */ 8983 public function MirrorH($x='') { 8984 $this->Scale(-100, 100, $x); 8985 } 8986 8987 /** 8988 * Verical Mirroring. 8989 * @param int $y ordinate of the point. Default is current y position 8990 * @access public 8991 * @since 2.1.000 (2008-01-07) 8992 * @see StartTransform(), StopTransform() 8993 */ 8994 public function MirrorV($y='') { 8995 $this->Scale(100, -100, '', $y); 8996 } 8997 8998 /** 8999 * Point reflection mirroring. 9000 * @param int $x abscissa of the point. Default is current x position 9001 * @param int $y ordinate of the point. Default is current y position 9002 * @access public 9003 * @since 2.1.000 (2008-01-07) 9004 * @see StartTransform(), StopTransform() 9005 */ 9006 public function MirrorP($x='',$y='') { 9007 $this->Scale(-100, -100, $x, $y); 9008 } 9009 9010 /** 9011 * Reflection against a straight line through point (x, y) with the gradient angle (angle). 9012 * @param float $angle gradient angle of the straight line. Default is 0 (horizontal line). 9013 * @param int $x abscissa of the point. Default is current x position 9014 * @param int $y ordinate of the point. Default is current y position 9015 * @access public 9016 * @since 2.1.000 (2008-01-07) 9017 * @see StartTransform(), StopTransform() 9018 */ 9019 public function MirrorL($angle=0, $x='',$y='') { 9020 $this->Scale(-100, 100, $x, $y); 9021 $this->Rotate(-2*($angle-90), $x, $y); 9022 } 9023 9024 /** 9025 * Translate graphic object horizontally. 9026 * @param int $t_x movement to the right (or left for RTL) 9027 * @access public 9028 * @since 2.1.000 (2008-01-07) 9029 * @see StartTransform(), StopTransform() 9030 */ 9031 public function TranslateX($t_x) { 9032 $this->Translate($t_x, 0); 9033 } 9034 9035 /** 9036 * Translate graphic object vertically. 9037 * @param int $t_y movement to the bottom 9038 * @access public 9039 * @since 2.1.000 (2008-01-07) 9040 * @see StartTransform(), StopTransform() 9041 */ 9042 public function TranslateY($t_y) { 9043 $this->Translate(0, $t_y); 9044 } 9045 9046 /** 9047 * Translate graphic object horizontally and vertically. 9048 * @param int $t_x movement to the right 9049 * @param int $t_y movement to the bottom 9050 * @access public 9051 * @since 2.1.000 (2008-01-07) 9052 * @see StartTransform(), StopTransform() 9053 */ 9054 public function Translate($t_x, $t_y) { 9055 //calculate elements of transformation matrix 9056 $tm[0] = 1; 9057 $tm[1] = 0; 9058 $tm[2] = 0; 9059 $tm[3] = 1; 9060 $tm[4] = $t_x * $this->k; 9061 $tm[5] = -$t_y * $this->k; 9062 //translate the coordinate system 9063 $this->Transform($tm); 9064 } 9065 9066 /** 9067 * Rotate object. 9068 * @param float $angle angle in degrees for counter-clockwise rotation 9069 * @param int $x abscissa of the rotation center. Default is current x position 9070 * @param int $y ordinate of the rotation center. Default is current y position 9071 * @access public 9072 * @since 2.1.000 (2008-01-07) 9073 * @see StartTransform(), StopTransform() 9074 */ 9075 public function Rotate($angle, $x='', $y='') { 9076 if ($x === '') { 9077 $x = $this->x; 9078 } 9079 if ($y === '') { 9080 $y = $this->y; 9081 } 9082 $y = ($this->h - $y) * $this->k; 9083 $x *= $this->k; 9084 //calculate elements of transformation matrix 9085 $tm[0] = cos(deg2rad($angle)); 9086 $tm[1] = sin(deg2rad($angle)); 9087 $tm[2] = -$tm[1]; 9088 $tm[3] = $tm[0]; 9089 $tm[4] = $x + ($tm[1] * $y) - ($tm[0] * $x); 9090 $tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x); 9091 //rotate the coordinate system around ($x,$y) 9092 $this->Transform($tm); 9093 } 9094 9095 /** 9096 * Skew horizontally. 9097 * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right) 9098 * @param int $x abscissa of the skewing center. default is current x position 9099 * @param int $y ordinate of the skewing center. default is current y position 9100 * @access public 9101 * @since 2.1.000 (2008-01-07) 9102 * @see StartTransform(), StopTransform() 9103 */ 9104 public function SkewX($angle_x, $x='', $y='') { 9105 $this->Skew($angle_x, 0, $x, $y); 9106 } 9107 9108 /** 9109 * Skew vertically. 9110 * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top) 9111 * @param int $x abscissa of the skewing center. default is current x position 9112 * @param int $y ordinate of the skewing center. default is current y position 9113 * @access public 9114 * @since 2.1.000 (2008-01-07) 9115 * @see StartTransform(), StopTransform() 9116 */ 9117 public function SkewY($angle_y, $x='', $y='') { 9118 $this->Skew(0, $angle_y, $x, $y); 9119 } 9120 9121 /** 9122 * Skew. 9123 * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right) 9124 * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top) 9125 * @param int $x abscissa of the skewing center. default is current x position 9126 * @param int $y ordinate of the skewing center. default is current y position 9127 * @access public 9128 * @since 2.1.000 (2008-01-07) 9129 * @see StartTransform(), StopTransform() 9130 */ 9131 public function Skew($angle_x, $angle_y, $x='', $y='') { 9132 if ($x === '') { 9133 $x = $this->x; 9134 } 9135 if ($y === '') { 9136 $y = $this->y; 9137 } 9138 if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) { 9139 $this->Error('Please use values between -90 and +90 degrees for Skewing.'); 9140 } 9141 $x *= $this->k; 9142 $y = ($this->h - $y) * $this->k; 9143 //calculate elements of transformation matrix 9144 $tm[0] = 1; 9145 $tm[1] = tan(deg2rad($angle_y)); 9146 $tm[2] = tan(deg2rad($angle_x)); 9147 $tm[3] = 1; 9148 $tm[4] = -$tm[2] * $y; 9149 $tm[5] = -$tm[1] * $x; 9150 //skew the coordinate system 9151 $this->Transform($tm); 9152 } 9153 9154 /** 9155 * Apply graphic transformations. 9156 * @param array $tm transformation matrix 9157 * @access protected 9158 * @since 2.1.000 (2008-01-07) 9159 * @see StartTransform(), StopTransform() 9160 */ 9161 protected function Transform($tm) { 9162 $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5])); 9163 // add tranformation matrix 9164 $this->transfmatrix[$this->transfmatrix_key][] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]); 9165 // update tranformation mark 9166 if (end($this->transfmrk[$this->page]) !== false) { 9167 $key = key($this->transfmrk[$this->page]); 9168 $this->transfmrk[$this->page][$key] = $this->pagelen[$this->page]; 9169 } 9170 } 9171 9172 // END TRANSFORMATIONS SECTION ------------------------- 9173 9174 9175 // START GRAPHIC FUNCTIONS SECTION --------------------- 9176 // The following section is based on the code provided by David Hernandez Sanz 9177 9178 /** 9179 * Defines the line width. By default, the value equals 0.2 mm. The method can be called before the first page is created and the value is retained from page to page. 9180 * @param float $width The width. 9181 * @access public 9182 * @since 1.0 9183 * @see Line(), Rect(), Cell(), MultiCell() 9184 */ 9185 public function SetLineWidth($width) { 9186 //Set line width 9187 $this->LineWidth = $width; 9188 $this->linestyleWidth = sprintf('%.2F w', ($width * $this->k)); 9189 if ($this->page > 0) { 9190 $this->_out($this->linestyleWidth); 9191 } 9192 } 9193 9194 /** 9195 * Returns the current the line width. 9196 * @return int Line width 9197 * @access public 9198 * @since 2.1.000 (2008-01-07) 9199 * @see Line(), SetLineWidth() 9200 */ 9201 public function GetLineWidth() { 9202 return $this->LineWidth; 9203 } 9204 9205 /** 9206 * Set line style. 9207 * @param array $style Line style. Array with keys among the following: 9208 * <ul> 9209 * <li>width (float): Width of the line in user units.</li> 9210 * <li>cap (string): Type of cap to put on the line. Possible values are: 9211 * butt, round, square. The difference between "square" and "butt" is that 9212 * "square" projects a flat end past the end of the line.</li> 9213 * <li>join (string): Type of join. Possible values are: miter, round, 9214 * bevel.</li> 9215 * <li>dash (mixed): Dash pattern. Is 0 (without dash) or string with 9216 * series of length values, which are the lengths of the on and off dashes. 9217 * For example: "2" represents 2 on, 2 off, 2 on, 2 off, ...; "2,1" is 2 on, 9218 * 1 off, 2 on, 1 off, ...</li> 9219 * <li>phase (integer): Modifier on the dash pattern which is used to shift 9220 * the point at which the pattern starts.</li> 9221 * <li>color (array): Draw color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K).</li> 9222 * </ul> 9223 * @access public 9224 * @since 2.1.000 (2008-01-08) 9225 */ 9226 public function SetLineStyle($style) { 9227 if (!is_array($style)) { 9228 return; 9229 } 9230 extract($style); 9231 if (isset($width)) { 9232 $width_prev = $this->LineWidth; 9233 $this->SetLineWidth($width); 9234 $this->LineWidth = $width_prev; 9235 } 9236 if (isset($cap)) { 9237 $ca = array('butt' => 0, 'round'=> 1, 'square' => 2); 9238 if (isset($ca[$cap])) { 9239 $this->linestyleCap = $ca[$cap].' J'; 9240 $this->_out($this->linestyleCap); 9241 } 9242 } 9243 if (isset($join)) { 9244 $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2); 9245 if (isset($ja[$join])) { 9246 $this->linestyleJoin = $ja[$join].' j'; 9247 $this->_out($this->linestyleJoin); 9248 } 9249 } 9250 if (isset($dash)) { 9251 $dash_string = ''; 9252 if ($dash) { 9253 if (preg_match('/^.+,/', $dash) > 0) { 9254 $tab = explode(',', $dash); 9255 } else { 9256 $tab = array($dash); 9257 } 9258 $dash_string = ''; 9259 foreach ($tab as $i => $v) { 9260 if ($i) { 9261 $dash_string .= ' '; 9262 } 9263 $dash_string .= sprintf('%.2F', $v); 9264 } 9265 } 9266 if (!isset($phase) OR !$dash) { 9267 $phase = 0; 9268 } 9269 $this->linestyleDash = sprintf('[%s] %.2F d', $dash_string, $phase); 9270 $this->_out($this->linestyleDash); 9271 } 9272 if (isset($color)) { 9273 $this->SetDrawColorArray($color); 9274 } 9275 } 9276 9277 /** 9278 * Begin a new subpath by moving the current point to coordinates (x, y), omitting any connecting line segment. 9279 * @param float $x Abscissa of point. 9280 * @param float $y Ordinate of point. 9281 * @access protected 9282 * @since 2.1.000 (2008-01-08) 9283 */ 9284 protected function _outPoint($x, $y) { 9285 $this->_out(sprintf('%.2F %.2F m', $x * $this->k, ($this->h - $y) * $this->k)); 9286 } 9287 9288 /** 9289 * Append a straight line segment from the current point to the point (x, y). 9290 * The new current point shall be (x, y). 9291 * @param float $x Abscissa of end point. 9292 * @param float $y Ordinate of end point. 9293 * @access protected 9294 * @since 2.1.000 (2008-01-08) 9295 */ 9296 protected function _outLine($x, $y) { 9297 $this->_out(sprintf('%.2F %.2F l', $x * $this->k, ($this->h - $y) * $this->k)); 9298 } 9299 9300 /** 9301 * Append a rectangle to the current path as a complete subpath, with lower-left corner (x, y) and dimensions widthand height in user space. 9302 * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language). 9303 * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language). 9304 * @param float $w Width. 9305 * @param float $h Height. 9306 * @param string $op options 9307 * @access protected 9308 * @since 2.1.000 (2008-01-08) 9309 */ 9310 protected function _outRect($x, $y, $w, $h, $op) { 9311 $this->_out(sprintf('%.2F %.2F %.2F %.2F re %s', $x * $this->k, ($this->h - $y) * $this->k, $w * $this->k, -$h * $this->k, $op)); 9312 } 9313 9314 /** 9315 * Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (x3, y3), using (x1, y1) and (x2, y2) as the Bézier control points. 9316 * The new current point shall be (x3, y3). 9317 * @param float $x1 Abscissa of control point 1. 9318 * @param float $y1 Ordinate of control point 1. 9319 * @param float $x2 Abscissa of control point 2. 9320 * @param float $y2 Ordinate of control point 2. 9321 * @param float $x3 Abscissa of end point. 9322 * @param float $y3 Ordinate of end point. 9323 * @access protected 9324 * @since 2.1.000 (2008-01-08) 9325 */ 9326 protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) { 9327 $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)); 9328 } 9329 9330 /** 9331 * Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (x3, y3), using the current point and (x2, y2) as the Bézier control points. 9332 * The new current point shall be (x3, y3). 9333 * @param float $x2 Abscissa of control point 2. 9334 * @param float $y2 Ordinate of control point 2. 9335 * @param float $x3 Abscissa of end point. 9336 * @param float $y3 Ordinate of end point. 9337 * @access protected 9338 * @since 4.9.019 (2010-04-26) 9339 */ 9340 protected function _outCurveV($x2, $y2, $x3, $y3) { 9341 $this->_out(sprintf('%.2F %.2F %.2F %.2F v', $x2 * $this->k, ($this->h - $y2) * $this->k, $x3 * $this->k, ($this->h - $y3) * $this->k)); 9342 } 9343 9344 /** 9345 * Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (x3, y3), using (x1, y1) and (x3, y3) as the Bézier control points. 9346 * The new current point shall be (x3, y3). 9347 * @param float $x1 Abscissa of control point 1. 9348 * @param float $y1 Ordinate of control point 1. 9349 * @param float $x2 Abscissa of control point 2. 9350 * @param float $y2 Ordinate of control point 2. 9351 * @param float $x3 Abscissa of end point. 9352 * @param float $y3 Ordinate of end point. 9353 * @access protected 9354 * @since 2.1.000 (2008-01-08) 9355 */ 9356 protected function _outCurveY($x1, $y1, $x3, $y3) { 9357 $this->_out(sprintf('%.2F %.2F %.2F %.2F y', $x1 * $this->k, ($this->h - $y1) * $this->k, $x3 * $this->k, ($this->h - $y3) * $this->k)); 9358 } 9359 9360 /** 9361 * Draws a line between two points. 9362 * @param float $x1 Abscissa of first point. 9363 * @param float $y1 Ordinate of first point. 9364 * @param float $x2 Abscissa of second point. 9365 * @param float $y2 Ordinate of second point. 9366 * @param array $style Line style. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). 9367 * @access public 9368 * @since 1.0 9369 * @see SetLineWidth(), SetDrawColor(), SetLineStyle() 9370 */ 9371 public function Line($x1, $y1, $x2, $y2, $style=array()) { 9372 if (is_array($style)) { 9373 $this->SetLineStyle($style); 9374 } 9375 $this->_outPoint($x1, $y1); 9376 $this->_outLine($x2, $y2); 9377 $this->_out('S'); 9378 } 9379 9380 /** 9381 * Draws a rectangle. 9382 * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language). 9383 * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language). 9384 * @param float $w Width. 9385 * @param float $h Height. 9386 * @param string $style Style of rendering. See the getPathPaintOperator() function for more information. 9387 * @param array $border_style Border style of rectangle. Array with keys among the following: 9388 * <ul> 9389 * <li>all: Line style of all borders. Array like for {@link SetLineStyle SetLineStyle}.</li> 9390 * <li>L, T, R, B or combinations: Line style of left, top, right or bottom border. Array like for {@link SetLineStyle SetLineStyle}.</li> 9391 * </ul> 9392 * If a key is not present or is null, not draws the border. Default value: default line style (empty array). 9393 * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). 9394 * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array). 9395 * @access public 9396 * @since 1.0 9397 * @see SetLineStyle() 9398 */ 9399 public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) { 9400 if (!(false === strpos($style, 'F')) AND !empty($fill_color)) { 9401 $this->SetFillColorArray($fill_color); 9402 } 9403 $op = $this->getPathPaintOperator($style); 9404 if ((!$border_style) OR (isset($border_style['all']))) { 9405 if (isset($border_style['all']) AND $border_style['all']) { 9406 $this->SetLineStyle($border_style['all']); 9407 $border_style = array(); 9408 } 9409 } 9410 $this->_outRect($x, $y, $w, $h, $op); 9411 9412 if ($border_style) { 9413 $border_style2 = array(); 9414 foreach ($border_style as $line => $value) { 9415 $length = strlen($line); 9416 for ($i = 0; $i < $length; ++$i) { 9417 $border_style2[$line[$i]] = $value; 9418 } 9419 } 9420 $border_style = $border_style2; 9421 if (isset($border_style['L']) AND $border_style['L']) { 9422 $this->Line($x, $y, $x, $y + $h, $border_style['L']); 9423 } 9424 if (isset($border_style['T']) AND $border_style['T']) { 9425 $this->Line($x, $y, $x + $w, $y, $border_style['T']); 9426 } 9427 if (isset($border_style['R']) AND $border_style['R']) { 9428 $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style['R']); 9429 } 9430 if (isset($border_style['B']) AND $border_style['B']) { 9431 $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style['B']); 9432 } 9433 } 9434 } 9435 9436 /** 9437 * Draws a Bezier curve. 9438 * The Bezier curve is a tangent to the line between the control points at 9439 * either end of the curve. 9440 * @param float $x0 Abscissa of start point. 9441 * @param float $y0 Ordinate of start point. 9442 * @param float $x1 Abscissa of control point 1. 9443 * @param float $y1 Ordinate of control point 1. 9444 * @param float $x2 Abscissa of control point 2. 9445 * @param float $y2 Ordinate of control point 2. 9446 * @param float $x3 Abscissa of end point. 9447 * @param float $y3 Ordinate of end point. 9448 * @param string $style Style of rendering. See the getPathPaintOperator() function for more information. 9449 * @param array $line_style Line style of curve. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). 9450 * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array). 9451 * @access public 9452 * @see SetLineStyle() 9453 * @since 2.1.000 (2008-01-08) 9454 */ 9455 public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) { 9456 if (!(false === strpos($style, 'F')) AND isset($fill_color)) { 9457 $this->SetFillColorArray($fill_color); 9458 } 9459 $op = $this->getPathPaintOperator($style); 9460 if ($line_style) { 9461 $this->SetLineStyle($line_style); 9462 } 9463 $this->_outPoint($x0, $y0); 9464 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3); 9465 $this->_out($op); 9466 } 9467 9468 /** 9469 * Draws a poly-Bezier curve. 9470 * Each Bezier curve segment is a tangent to the line between the control points at 9471 * either end of the curve. 9472 * @param float $x0 Abscissa of start point. 9473 * @param float $y0 Ordinate of start point. 9474 * @param float $segments An array of bezier descriptions. Format: array(x1, y1, x2, y2, x3, y3). 9475 * @param string $style Style of rendering. See the getPathPaintOperator() function for more information. 9476 * @param array $line_style Line style of curve. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). 9477 * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array). 9478 * @access public 9479 * @see SetLineStyle() 9480 * @since 3.0008 (2008-05-12) 9481 */ 9482 public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) { 9483 if (!(false === strpos($style, 'F')) AND isset($fill_color)) { 9484 $this->SetFillColorArray($fill_color); 9485 } 9486 $op = $this->getPathPaintOperator($style); 9487 if ($op == 'f') { 9488 $line_style = array(); 9489 } 9490 if ($line_style) { 9491 $this->SetLineStyle($line_style); 9492 } 9493 $this->_outPoint($x0, $y0); 9494 foreach ($segments as $segment) { 9495 list($x1, $y1, $x2, $y2, $x3, $y3) = $segment; 9496 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3); 9497 } 9498 $this->_out($op); 9499 } 9500 9501 /** 9502 * Draws an ellipse. 9503 * An ellipse is formed from n Bezier curves. 9504 * @param float $x0 Abscissa of center point. 9505 * @param float $y0 Ordinate of center point. 9506 * @param float $rx Horizontal radius. 9507 * @param float $ry Vertical radius (if ry = 0 then is a circle, see {@link Circle Circle}). Default value: 0. 9508 * @param float $angle: Angle oriented (anti-clockwise). Default value: 0. 9509 * @param float $astart: Angle start of draw line. Default value: 0. 9510 * @param float $afinish: Angle finish of draw line. Default value: 360. 9511 * @param string $style Style of rendering. See the getPathPaintOperator() function for more information. 9512 * @param array $line_style Line style of ellipse. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). 9513 * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array). 9514 * @param integer $nc Number of curves used to draw a 90 degrees portion of ellipse. 9515 * @author Nicola Asuni 9516 * @access public 9517 * @since 2.1.000 (2008-01-08) 9518 */ 9519 public function Ellipse($x0, $y0, $rx, $ry='', $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) { 9520 if ($this->empty_string($ry) OR ($ry == 0)) { 9521 $ry = $rx; 9522 } 9523 if (!(false === strpos($style, 'F')) AND isset($fill_color)) { 9524 $this->SetFillColorArray($fill_color); 9525 } 9526 $op = $this->getPathPaintOperator($style); 9527 if ($op == 'f') { 9528 $line_style = array(); 9529 } 9530 if ($line_style) { 9531 $this->SetLineStyle($line_style); 9532 } 9533 $this->_outellipticalarc($x0, $y0, $rx, $ry, $angle, $astart, $afinish, false, $nc); 9534 $this->_out($op); 9535 } 9536 9537 /** 9538 * Append an elliptical arc to the current path. 9539 * An ellipse is formed from n Bezier curves. 9540 * @param float $xc Abscissa of center point. 9541 * @param float $yc Ordinate of center point. 9542 * @param float $rx Horizontal radius. 9543 * @param float $ry Vertical radius (if ry = 0 then is a circle, see {@link Circle Circle}). Default value: 0. 9544 * @param float $xang: Angle between the X-axis and the major axis of the ellipse. Default value: 0. 9545 * @param float $angs: Angle start of draw line. Default value: 0. 9546 * @param float $angf: Angle finish of draw line. Default value: 360. 9547 * @param boolean $pie if true do not mark the border point (used to draw pie sectors). 9548 * @param integer $nc Number of curves used to draw a 90 degrees portion of ellipse. 9549 * @author Nicola Asuni 9550 * @access protected 9551 * @since 4.9.019 (2010-04-26) 9552 */ 9553 protected function _outellipticalarc($xc, $yc, $rx, $ry, $xang=0, $angs=0, $angf=360, $pie=false, $nc=2) { 9554 $k = $this->k; 9555 if ($nc < 2) { 9556 $nc = 2; 9557 } 9558 if ($pie) { 9559 // center of the arc 9560 $this->_outPoint($xc, $yc); 9561 } 9562 $xang = deg2rad((float) $xang); 9563 $angs = deg2rad((float) $angs); 9564 $angf = deg2rad((float) $angf); 9565 $as = atan2((sin($angs) / $ry), (cos($angs) / $rx)); 9566 $af = atan2((sin($angf) / $ry), (cos($angf) / $rx)); 9567 if ($as < 0) { 9568 $as += (2 * M_PI); 9569 } 9570 if ($af < 0) { 9571 $af += (2 * M_PI); 9572 } 9573 if ($as > $af) { 9574 // reverse rotation go clockwise 9575 $as -= (2 * M_PI); 9576 } 9577 $total_angle = ($af - $as); 9578 if ($nc < 2) { 9579 $nc = 2; 9580 } 9581 // total arcs to draw 9582 $nc *= (2 * abs($total_angle) / M_PI); 9583 $nc = round($nc) + 1; 9584 // angle of each arc 9585 $arcang = $total_angle / $nc; 9586 // center point in PDF coordiantes 9587 $x0 = $xc; 9588 $y0 = ($this->h - $yc); 9589 // starting angle 9590 $ang = $as; 9591 $alpha = sin($arcang) * ((sqrt(4 + (3 * pow(tan(($arcang) / 2), 2))) - 1) / 3); 9592 $cos_xang = cos($xang); 9593 $sin_xang = sin($xang); 9594 $cos_ang = cos($ang); 9595 $sin_ang = sin($ang); 9596 // first arc point 9597 $px1 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang); 9598 $py1 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang); 9599 // first Bezier control point 9600 $qx1 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang))); 9601 $qy1 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang))); 9602 if ($pie) { 9603 $this->_outLine($px1, $this->h - $py1); 9604 } else { 9605 $this->_outPoint($px1, $this->h - $py1); 9606 } 9607 // draw arcs 9608 for ($i = 1; $i <= $nc; ++$i) { 9609 // starting angle 9610 $ang = $as + ($i * $arcang); 9611 $cos_xang = cos($xang); 9612 $sin_xang = sin($xang); 9613 $cos_ang = cos($ang); 9614 $sin_ang = sin($ang); 9615 // second arc point 9616 $px2 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang); 9617 $py2 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang); 9618 // second Bezier control point 9619 $qx2 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang))); 9620 $qy2 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang))); 9621 // draw arc 9622 $this->_outCurve(($px1 + $qx1), ($this->h - ($py1 + $qy1)), ($px2 - $qx2), ($this->h - ($py2 - $qy2)), $px2, ($this->h - $py2)); 9623 // move to next point 9624 $px1 = $px2; 9625 $py1 = $py2; 9626 $qx1 = $qx2; 9627 $qy1 = $qy2; 9628 } 9629 if ($pie) { 9630 $this->_outLine($xc, $yc); 9631 } 9632 } 9633 9634 9635 /** 9636 * Draws a circle. 9637 * A circle is formed from n Bezier curves. 9638 * @param float $x0 Abscissa of center point. 9639 * @param float $y0 Ordinate of center point. 9640 * @param float $r Radius. 9641 * @param float $angstr: Angle start of draw line. Default value: 0. 9642 * @param float $angend: Angle finish of draw line. Default value: 360. 9643 * @param string $style Style of rendering. See the getPathPaintOperator() function for more information. 9644 * @param array $line_style Line style of circle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). 9645 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). 9646 * @param integer $nc Number of curves used to draw a 90 degrees portion of circle. 9647 * @access public 9648 * @since 2.1.000 (2008-01-08) 9649 */ 9650 public function Circle($x0, $y0, $r, $angstr=0, $angend=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) { 9651 $this->Ellipse($x0, $y0, $r, $r, 0, $angstr, $angend, $style, $line_style, $fill_color, $nc); 9652 } 9653 9654 /** 9655 * Draws a polygonal line 9656 * @param array $p Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1)) 9657 * @param string $style Style of rendering. See the getPathPaintOperator() function for more information. 9658 * @param array $line_style Line style of polygon. Array with keys among the following: 9659 * <ul> 9660 * <li>all: Line style of all lines. Array like for {@link SetLineStyle SetLineStyle}.</li> 9661 * <li>0 to ($np - 1): Line style of each line. Array like for {@link SetLineStyle SetLineStyle}.</li> 9662 * </ul> 9663 * If a key is not present or is null, not draws the line. Default value is default line style (empty array). 9664 * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array). 9665 * @param boolean $closed if true the polygon is closes, otherwise will remain open 9666 * @access public 9667 * @since 4.8.003 (2009-09-15) 9668 */ 9669 public function PolyLine($p, $style='', $line_style=array(), $fill_color=array()) { 9670 $this->Polygon($p, $style, $line_style, $fill_color, false); 9671 } 9672 9673 /** 9674 * Draws a polygon. 9675 * @param array $p Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1)) 9676 * @param string $style Style of rendering. See the getPathPaintOperator() function for more information. 9677 * @param array $line_style Line style of polygon. Array with keys among the following: 9678 * <ul> 9679 * <li>all: Line style of all lines. Array like for {@link SetLineStyle SetLineStyle}.</li> 9680 * <li>0 to ($np - 1): Line style of each line. Array like for {@link SetLineStyle SetLineStyle}.</li> 9681 * </ul> 9682 * If a key is not present or is null, not draws the line. Default value is default line style (empty array). 9683 * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array). 9684 * @param boolean $closed if true the polygon is closes, otherwise will remain open 9685 * @access public 9686 * @since 2.1.000 (2008-01-08) 9687 */ 9688 public function Polygon($p, $style='', $line_style=array(), $fill_color=array(), $closed=true) { 9689 $nc = count($p); // number of coordinates 9690 $np = $nc / 2; // number of points 9691 if ($closed) { 9692 // close polygon by adding the first 2 points at the end (one line) 9693 for ($i = 0; $i < 4; ++$i) { 9694 $p[$nc + $i] = $p[$i]; 9695 } 9696 // copy style for the last added line 9697 if (isset($line_style[0])) { 9698 $line_style[$np] = $line_style[0]; 9699 } 9700 $nc += 4; 9701 } 9702 if (!(false === strpos($style, 'F')) AND isset($fill_color)) { 9703 $this->SetFillColorArray($fill_color); 9704 } 9705 $op = $this->getPathPaintOperator($style); 9706 if ($op == 'f') { 9707 $line_style = array(); 9708 } 9709 $draw = true; 9710 if ($line_style) { 9711 if (isset($line_style['all'])) { 9712 $this->SetLineStyle($line_style['all']); 9713 } else { 9714 $draw = false; 9715 if ($op == 'B') { 9716 // draw fill 9717 $op = 'f'; 9718 $this->_outPoint($p[0], $p[1]); 9719 for ($i = 2; $i < $nc; $i = $i + 2) { 9720 $this->_outLine($p[$i], $p[$i + 1]); 9721 } 9722 $this->_out($op); 9723 } 9724 // draw outline 9725 $this->_outPoint($p[0], $p[1]); 9726 for ($i = 2; $i < $nc; $i = $i + 2) { 9727 $line_num = ($i / 2) - 1; 9728 if (isset($line_style[$line_num])) { 9729 if ($line_style[$line_num] != 0) { 9730 if (is_array($line_style[$line_num])) { 9731 $this->_out('S'); 9732 $this->SetLineStyle($line_style[$line_num]); 9733 $this->_outPoint($p[$i - 2], $p[$i - 1]); 9734 $this->_outLine($p[$i], $p[$i + 1]); 9735 $this->_out('S'); 9736 $this->_outPoint($p[$i], $p[$i + 1]); 9737 } else { 9738 $this->_outLine($p[$i], $p[$i + 1]); 9739 } 9740 } 9741 } else { 9742 $this->_outLine($p[$i], $p[$i + 1]); 9743 } 9744 } 9745 $this->_out($op); 9746 } 9747 } 9748 if ($draw) { 9749 $this->_outPoint($p[0], $p[1]); 9750 for ($i = 2; $i < $nc; $i = $i + 2) { 9751 $this->_outLine($p[$i], $p[$i + 1]); 9752 } 9753 $this->_out($op); 9754 } 9755 } 9756 9757 /** 9758 * Draws a regular polygon. 9759 * @param float $x0 Abscissa of center point. 9760 * @param float $y0 Ordinate of center point. 9761 * @param float $r: Radius of inscribed circle. 9762 * @param integer $ns Number of sides. 9763 * @param float $angle Angle oriented (anti-clockwise). Default value: 0. 9764 * @param boolean $draw_circle Draw inscribed circle or not. Default value: false. 9765 * @param string $style Style of rendering. See the getPathPaintOperator() function for more information. 9766 * @param array $line_style Line style of polygon sides. Array with keys among the following: 9767 * <ul> 9768 * <li>all: Line style of all sides. Array like for {@link SetLineStyle SetLineStyle}.</li> 9769 * <li>0 to ($ns - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li> 9770 * </ul> 9771 * If a key is not present or is null, not draws the side. Default value is default line style (empty array). 9772 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). 9773 * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are: 9774 * <ul> 9775 * <li>D or empty string: Draw (default).</li> 9776 * <li>F: Fill.</li> 9777 * <li>DF or FD: Draw and fill.</li> 9778 * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li> 9779 * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li> 9780 * </ul> 9781 * @param array $circle_outLine_style Line style of inscribed circle (if draws). Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). 9782 * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array). 9783 * @access public 9784 * @since 2.1.000 (2008-01-08) 9785 */ 9786 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()) { 9787 if (3 > $ns) { 9788 $ns = 3; 9789 } 9790 if ($draw_circle) { 9791 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color); 9792 } 9793 $p = array(); 9794 for ($i = 0; $i < $ns; ++$i) { 9795 $a = $angle + ($i * 360 / $ns); 9796 $a_rad = deg2rad((float) $a); 9797 $p[] = $x0 + ($r * sin($a_rad)); 9798 $p[] = $y0 + ($r * cos($a_rad)); 9799 } 9800 $this->Polygon($p, $style, $line_style, $fill_color); 9801 } 9802 9803 /** 9804 * Draws a star polygon 9805 * @param float $x0 Abscissa of center point. 9806 * @param float $y0 Ordinate of center point. 9807 * @param float $r Radius of inscribed circle. 9808 * @param integer $nv Number of vertices. 9809 * @param integer $ng Number of gap (if ($ng % $nv = 1) then is a regular polygon). 9810 * @param float $angle: Angle oriented (anti-clockwise). Default value: 0. 9811 * @param boolean $draw_circle: Draw inscribed circle or not. Default value is false. 9812 * @param string $style Style of rendering. See the getPathPaintOperator() function for more information. 9813 * @param array $line_style Line style of polygon sides. Array with keys among the following: 9814 * <ul> 9815 * <li>all: Line style of all sides. Array like for 9816 * {@link SetLineStyle SetLineStyle}.</li> 9817 * <li>0 to (n - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li> 9818 * </ul> 9819 * If a key is not present or is null, not draws the side. Default value is default line style (empty array). 9820 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). 9821 * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are: 9822 * <ul> 9823 * <li>D or empty string: Draw (default).</li> 9824 * <li>F: Fill.</li> 9825 * <li>DF or FD: Draw and fill.</li> 9826 * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li> 9827 * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li> 9828 * </ul> 9829 * @param array $circle_outLine_style Line style of inscribed circle (if draws). Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). 9830 * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array). 9831 * @access public 9832 * @since 2.1.000 (2008-01-08) 9833 */ 9834 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()) { 9835 if ($nv < 2) { 9836 $nv = 2; 9837 } 9838 if ($draw_circle) { 9839 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color); 9840 } 9841 $p2 = array(); 9842 $visited = array(); 9843 for ($i = 0; $i < $nv; ++$i) { 9844 $a = $angle + ($i * 360 / $nv); 9845 $a_rad = deg2rad((float) $a); 9846 $p2[] = $x0 + ($r * sin($a_rad)); 9847 $p2[] = $y0 + ($r * cos($a_rad)); 9848 $visited[] = false; 9849 } 9850 $p = array(); 9851 $i = 0; 9852 do { 9853 $p[] = $p2[$i * 2]; 9854 $p[] = $p2[($i * 2) + 1]; 9855 $visited[$i] = true; 9856 $i += $ng; 9857 $i %= $nv; 9858 } while (!$visited[$i]); 9859 $this->Polygon($p, $style, $line_style, $fill_color); 9860 } 9861 9862 /** 9863 * Draws a rounded rectangle. 9864 * @param float $x Abscissa of upper-left corner. 9865 * @param float $y Ordinate of upper-left corner. 9866 * @param float $w Width. 9867 * @param float $h Height. 9868 * @param float $r the radius of the circle used to round off the corners of the rectangle. 9869 * @param string $round_corner Draws rounded corner or not. String with a 0 (not rounded i-corner) or 1 (rounded i-corner) in i-position. Positions are, in order and begin to 0: top left, top right, bottom right and bottom left. Default value: all rounded corner ("1111"). 9870 * @param string $style Style of rendering. See the getPathPaintOperator() function for more information. 9871 * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). 9872 * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array). 9873 * @access public 9874 * @since 2.1.000 (2008-01-08) 9875 */ 9876 public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) { 9877 $this->RoundedRectXY($x, $y, $w, $h, $r, $r, $round_corner, $style, $border_style, $fill_color); 9878 } 9879 9880 /** 9881 * Draws a rounded rectangle. 9882 * @param float $x Abscissa of upper-left corner. 9883 * @param float $y Ordinate of upper-left corner. 9884 * @param float $w Width. 9885 * @param float $h Height. 9886 * @param float $rx the x-axis radius of the ellipse used to round off the corners of the rectangle. 9887 * @param float $ry the y-axis radius of the ellipse used to round off the corners of the rectangle. 9888 * @param string $round_corner Draws rounded corner or not. String with a 0 (not rounded i-corner) or 1 (rounded i-corner) in i-position. Positions are, in order and begin to 0: top left, top right, bottom right and bottom left. Default value: all rounded corner ("1111"). 9889 * @param string $style Style of rendering. See the getPathPaintOperator() function for more information. 9890 * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). 9891 * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array). 9892 * @access public 9893 * @since 4.9.019 (2010-04-22) 9894 */ 9895 public function RoundedRectXY($x, $y, $w, $h, $rx, $ry, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) { 9896 if (($round_corner == '0000') OR (($rx == $ry) AND ($rx == 0))) { 9897 // Not rounded 9898 $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color); 9899 return; 9900 } 9901 // Rounded 9902 if (!(false === strpos($style, 'F')) AND isset($fill_color)) { 9903 $this->SetFillColorArray($fill_color); 9904 } 9905 $op = $this->getPathPaintOperator($style); 9906 if ($op == 'f') { 9907 $border_style = array(); 9908 } 9909 if ($border_style) { 9910 $this->SetLineStyle($border_style); 9911 } 9912 $MyArc = 4 / 3 * (sqrt(2) - 1); 9913 $this->_outPoint($x + $rx, $y); 9914 $xc = $x + $w - $rx; 9915 $yc = $y + $ry; 9916 $this->_outLine($xc, $y); 9917 if ($round_corner[0]) { 9918 $this->_outCurve($xc + ($rx * $MyArc), $yc - $ry, $xc + $rx, $yc - ($ry * $MyArc), $xc + $rx, $yc); 9919 } else { 9920 $this->_outLine($x + $w, $y); 9921 } 9922 $xc = $x + $w - $rx; 9923 $yc = $y + $h - $ry; 9924 $this->_outLine($x + $w, $yc); 9925 if ($round_corner[1]) { 9926 $this->_outCurve($xc + $rx, $yc + ($ry * $MyArc), $xc + ($rx * $MyArc), $yc + $ry, $xc, $yc + $ry); 9927 } else { 9928 $this->_outLine($x + $w, $y + $h); 9929 } 9930 $xc = $x + $rx; 9931 $yc = $y + $h - $ry; 9932 $this->_outLine($xc, $y + $h); 9933 if ($round_corner[2]) { 9934 $this->_outCurve($xc - ($rx * $MyArc), $yc + $ry, $xc - $rx, $yc + ($ry * $MyArc), $xc - $rx, $yc); 9935 } else { 9936 $this->_outLine($x, $y + $h); 9937 } 9938 $xc = $x + $rx; 9939 $yc = $y + $ry; 9940 $this->_outLine($x, $yc); 9941 if ($round_corner[3]) { 9942 $this->_outCurve($xc - $rx, $yc - ($ry * $MyArc), $xc - ($rx * $MyArc), $yc - $ry, $xc, $yc - $ry); 9943 } else { 9944 $this->_outLine($x, $y); 9945 $this->_outLine($x + $rx, $y); 9946 } 9947 $this->_out($op); 9948 } 9949 9950 /** 9951 * Draws a grahic arrow. 9952 * @parameter float $x0 Abscissa of first point. 9953 * @parameter float $y0 Ordinate of first point. 9954 * @parameter float $x0 Abscissa of second point. 9955 * @parameter float $y1 Ordinate of second point. 9956 * @parameter int $head_style (0 = draw only arrowhead arms, 1 = draw closed arrowhead, but no fill, 2 = closed and filled arrowhead, 3 = filled arrowhead) 9957 * @parameter float $arm_size length of arrowhead arms 9958 * @parameter int $arm_angle angle between an arm and the shaft 9959 * @author Piotr Galecki, Nicola Asuni, Andy Meier 9960 * @since 4.6.018 (2009-07-10) 9961 */ 9962 public function Arrow($x0, $y0, $x1, $y1, $head_style=0, $arm_size=5, $arm_angle=15) { 9963 // getting arrow direction angle 9964 // 0 deg angle is when both arms go along X axis. angle grows clockwise. 9965 $dir_angle = atan2(($y0 - $y1), ($x0 - $x1)); 9966 if ($dir_angle < 0) { 9967 $dir_angle += (2 * M_PI); 9968 } 9969 $arm_angle = deg2rad($arm_angle); 9970 $sx1 = $x1; 9971 $sy1 = $y1; 9972 if ($head_style > 0) { 9973 // calculate the stopping point for the arrow shaft 9974 $sx1 = $x1 + (($arm_size - $this->LineWidth) * cos($dir_angle)); 9975 $sy1 = $y1 + (($arm_size - $this->LineWidth) * sin($dir_angle)); 9976 } 9977 // main arrow line / shaft 9978 $this->Line($x0, $y0, $sx1, $sy1); 9979 // left arrowhead arm tip 9980 $x2L = $x1 + ($arm_size * cos($dir_angle + $arm_angle)); 9981 $y2L = $y1 + ($arm_size * sin($dir_angle + $arm_angle)); 9982 // right arrowhead arm tip 9983 $x2R = $x1 + ($arm_size * cos($dir_angle - $arm_angle)); 9984 $y2R = $y1 + ($arm_size * sin($dir_angle - $arm_angle)); 9985 $mode = 'D'; 9986 $style = array(); 9987 switch ($head_style) { 9988 case 0: { 9989 // draw only arrowhead arms 9990 $mode = 'D'; 9991 $style = array(1, 1, 0); 9992 break; 9993 } 9994 case 1: { 9995 // draw closed arrowhead, but no fill 9996 $mode = 'D'; 9997 break; 9998 } 9999 case 2: { 10000 // closed and filled arrowhead 10001 $mode = 'DF'; 10002 break; 10003 } 10004 case 3: { 10005 // filled arrowhead 10006 $mode = 'F'; 10007 break; 10008 } 10009 } 10010 $this->Polygon(array($x2L, $y2L, $x1, $y1, $x2R, $y2R), $mode, $style, array()); 10011 } 10012 10013 // END GRAPHIC FUNCTIONS SECTION ----------------------- 10014 10015 // BIDIRECTIONAL TEXT SECTION -------------------------- 10016 10017 /** 10018 * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/). 10019 * @param string $str string to manipulate. 10020 * @param bool $setbom if true set the Byte Order Mark (BOM = 0xFEFF) 10021 * @param bool $forcertl if true forces RTL text direction 10022 * @return string 10023 * @access protected 10024 * @author Nicola Asuni 10025 * @since 2.1.000 (2008-01-08) 10026 */ 10027 protected function utf8StrRev($str, $setbom=false, $forcertl=false) { 10028 return $this->utf8StrArrRev($this->UTF8StringToArray($str), $str, $setbom, $forcertl); 10029 } 10030 10031 /** 10032 * Reverse the RLT substrings array using the Bidirectional Algorithm (http://unicode.org/reports/tr9/). 10033 * @param array $arr array of unicode values. 10034 * @param string $str string to manipulate (or empty value). 10035 * @param bool $setbom if true set the Byte Order Mark (BOM = 0xFEFF) 10036 * @param bool $forcertl if true forces RTL text direction 10037 * @return string 10038 * @access protected 10039 * @author Nicola Asuni 10040 * @since 4.9.000 (2010-03-27) 10041 */ 10042 protected function utf8StrArrRev($arr, $str='', $setbom=false, $forcertl=false) { 10043 return $this->arrUTF8ToUTF16BE($this->utf8Bidi($arr, $str, $forcertl), $setbom); 10044 } 10045 10046 /** 10047 * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/). 10048 * @param array $ta array of characters composing the string. 10049 * @param string $str string to process 10050 * @param bool $forcertl if 'R' forces RTL, if 'L' forces LTR 10051 * @return array of unicode chars 10052 * @author Nicola Asuni 10053 * @access protected 10054 * @since 2.4.000 (2008-03-06) 10055 */ 10056 protected function utf8Bidi($ta, $str='', $forcertl=false) { 10057 global $unicode, $unicode_mirror, $unicode_arlet, $laa_array, $diacritics; 10058 // paragraph embedding level 10059 $pel = 0; 10060 // max level 10061 $maxlevel = 0; 10062 if ($this->empty_string($str)) { 10063 // create string from array 10064 $str = $this->UTF8ArrSubString($ta); 10065 } 10066 // check if string contains arabic text 10067 if (preg_match(K_RE_PATTERN_ARABIC, $str)) { 10068 $arabic = true; 10069 } else { 10070 $arabic = false; 10071 } 10072 // check if string contains RTL text 10073 if (!($forcertl OR $arabic OR preg_match(K_RE_PATTERN_RTL, $str))) { 10074 return $ta; 10075 } 10076 10077 // get number of chars 10078 $numchars = count($ta); 10079 10080 if ($forcertl == 'R') { 10081 $pel = 1; 10082 } elseif ($forcertl == 'L') { 10083 $pel = 0; 10084 } else { 10085 // P2. In each paragraph, find the first character of type L, AL, or R. 10086 // 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. 10087 for ($i=0; $i < $numchars; ++$i) { 10088 $type = $unicode[$ta[$i]]; 10089 if ($type == 'L') { 10090 $pel = 0; 10091 break; 10092 } elseif (($type == 'AL') OR ($type == 'R')) { 10093 $pel = 1; 10094 break; 10095 } 10096 } 10097 } 10098 10099 // Current Embedding Level 10100 $cel = $pel; 10101 // directional override status 10102 $dos = 'N'; 10103 $remember = array(); 10104 // start-of-level-run 10105 $sor = $pel % 2 ? 'R' : 'L'; 10106 $eor = $sor; 10107 10108 // Array of characters data 10109 $chardata = Array(); 10110 10111 // 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. 10112 // In the resolution of levels in rules I1 and I2, the maximum embedding level of 62 can be reached. 10113 for ($i=0; $i < $numchars; ++$i) { 10114 if ($ta[$i] == K_RLE) { 10115 // X2. With each RLE, compute the least greater odd embedding level. 10116 // 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. 10117 // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status. 10118 $next_level = $cel + ($cel % 2) + 1; 10119 if ($next_level < 62) { 10120 $remember[] = array('num' => K_RLE, 'cel' => $cel, 'dos' => $dos); 10121 $cel = $next_level; 10122 $dos = 'N'; 10123 $sor = $eor; 10124 $eor = $cel % 2 ? 'R' : 'L'; 10125 } 10126 } elseif ($ta[$i] == K_LRE) { 10127 // X3. With each LRE, compute the least greater even embedding level. 10128 // 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. 10129 // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status. 10130 $next_level = $cel + 2 - ($cel % 2); 10131 if ( $next_level < 62 ) { 10132 $remember[] = array('num' => K_LRE, 'cel' => $cel, 'dos' => $dos); 10133 $cel = $next_level; 10134 $dos = 'N'; 10135 $sor = $eor; 10136 $eor = $cel % 2 ? 'R' : 'L'; 10137 } 10138 } elseif ($ta[$i] == K_RLO) { 10139 // X4. With each RLO, compute the least greater odd embedding level. 10140 // 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. 10141 // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status. 10142 $next_level = $cel + ($cel % 2) + 1; 10143 if ($next_level < 62) { 10144 $remember[] = array('num' => K_RLO, 'cel' => $cel, 'dos' => $dos); 10145 $cel = $next_level; 10146 $dos = 'R'; 10147 $sor = $eor; 10148 $eor = $cel % 2 ? 'R' : 'L'; 10149 } 10150 } elseif ($ta[$i] == K_LRO) { 10151 // X5. With each LRO, compute the least greater even embedding level. 10152 // 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. 10153 // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status. 10154 $next_level = $cel + 2 - ($cel % 2); 10155 if ( $next_level < 62 ) { 10156 $remember[] = array('num' => K_LRO, 'cel' => $cel, 'dos' => $dos); 10157 $cel = $next_level; 10158 $dos = 'L'; 10159 $sor = $eor; 10160 $eor = $cel % 2 ? 'R' : 'L'; 10161 } 10162 } elseif ($ta[$i] == K_PDF) { 10163 // 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. 10164 if (count($remember)) { 10165 $last = count($remember ) - 1; 10166 if (($remember[$last]['num'] == K_RLE) OR 10167 ($remember[$last]['num'] == K_LRE) OR 10168 ($remember[$last]['num'] == K_RLO) OR 10169 ($remember[$last]['num'] == K_LRO)) { 10170 $match = array_pop($remember); 10171 $cel = $match['cel']; 10172 $dos = $match['dos']; 10173 $sor = $eor; 10174 $eor = ($cel > $match['cel'] ? $cel : $match['cel']) % 2 ? 'R' : 'L'; 10175 } 10176 } 10177 } elseif (($ta[$i] != K_RLE) AND 10178 ($ta[$i] != K_LRE) AND 10179 ($ta[$i] != K_RLO) AND 10180 ($ta[$i] != K_LRO) AND 10181 ($ta[$i] != K_PDF)) { 10182 // X6. For all types besides RLE, LRE, RLO, LRO, and PDF: 10183 // a. Set the level of the current character to the current embedding level. 10184 // b. Whenever the directional override status is not neutral, reset the current character type to the directional override status. 10185 if ($dos != 'N') { 10186 $chardir = $dos; 10187 } else { 10188 if (isset($unicode[$ta[$i]])) { 10189 $chardir = $unicode[$ta[$i]]; 10190 } else { 10191 $chardir = 'L'; 10192 } 10193 } 10194 // stores string characters and other information 10195 $chardata[] = array('char' => $ta[$i], 'level' => $cel, 'type' => $chardir, 'sor' => $sor, 'eor' => $eor); 10196 } 10197 } // end for each char 10198 10199 // X8. All explicit directional embeddings and overrides are completely terminated at the end of each paragraph. Paragraph separators are not included in the embedding. 10200 // X9. Remove all RLE, LRE, RLO, LRO, PDF, and BN codes. 10201 // 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. 10202 10203 // 3.3.3 Resolving Weak Types 10204 // 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. 10205 // Nonspacing marks are now resolved based on the previous characters. 10206 $numchars = count($chardata); 10207 10208 // 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. 10209 $prevlevel = -1; // track level changes 10210 $levcount = 0; // counts consecutive chars at the same level 10211 for ($i=0; $i < $numchars; ++$i) { 10212 if ($chardata[$i]['type'] == 'NSM') { 10213 if ($levcount) { 10214 $chardata[$i]['type'] = $chardata[$i]['sor']; 10215 } elseif ($i > 0) { 10216 $chardata[$i]['type'] = $chardata[($i-1)]['type']; 10217 } 10218 } 10219 if ($chardata[$i]['level'] != $prevlevel) { 10220 $levcount = 0; 10221 } else { 10222 ++$levcount; 10223 } 10224 $prevlevel = $chardata[$i]['level']; 10225 } 10226 10227 // 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. 10228 $prevlevel = -1; 10229 $levcount = 0; 10230 for ($i=0; $i < $numchars; ++$i) { 10231 if ($chardata[$i]['char'] == 'EN') { 10232 for ($j=$levcount; $j >= 0; $j--) { 10233 if ($chardata[$j]['type'] == 'AL') { 10234 $chardata[$i]['type'] = 'AN'; 10235 } elseif (($chardata[$j]['type'] == 'L') OR ($chardata[$j]['type'] == 'R')) { 10236 break; 10237 } 10238 } 10239 } 10240 if ($chardata[$i]['level'] != $prevlevel) { 10241 $levcount = 0; 10242 } else { 10243 ++$levcount; 10244 } 10245 $prevlevel = $chardata[$i]['level']; 10246 } 10247 10248 // W3. Change all ALs to R. 10249 for ($i=0; $i < $numchars; ++$i) { 10250 if ($chardata[$i]['type'] == 'AL') { 10251 $chardata[$i]['type'] = 'R'; 10252 } 10253 } 10254 10255 // 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. 10256 $prevlevel = -1; 10257 $levcount = 0; 10258 for ($i=0; $i < $numchars; ++$i) { 10259 if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) { 10260 if (($chardata[$i]['type'] == 'ES') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) { 10261 $chardata[$i]['type'] = 'EN'; 10262 } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) { 10263 $chardata[$i]['type'] = 'EN'; 10264 } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'AN') AND ($chardata[($i+1)]['type'] == 'AN')) { 10265 $chardata[$i]['type'] = 'AN'; 10266 } 10267 } 10268 if ($chardata[$i]['level'] != $prevlevel) { 10269 $levcount = 0; 10270 } else { 10271 ++$levcount; 10272 } 10273 $prevlevel = $chardata[$i]['level']; 10274 } 10275 10276 // W5. A sequence of European terminators adjacent to European numbers changes to all European numbers. 10277 $prevlevel = -1; 10278 $levcount = 0; 10279 for ($i=0; $i < $numchars; ++$i) { 10280 if ($chardata[$i]['type'] == 'ET') { 10281 if (($levcount > 0) AND ($chardata[($i-1)]['type'] == 'EN')) { 10282 $chardata[$i]['type'] = 'EN'; 10283 } else { 10284 $j = $i+1; 10285 while (($j < $numchars) AND ($chardata[$j]['level'] == $prevlevel)) { 10286 if ($chardata[$j]['type'] == 'EN') { 10287 $chardata[$i]['type'] = 'EN'; 10288 break; 10289 } elseif ($chardata[$j]['type'] != 'ET') { 10290 break; 10291 } 10292 ++$j; 10293 } 10294 } 10295 } 10296 if ($chardata[$i]['level'] != $prevlevel) { 10297 $levcount = 0; 10298 } else { 10299 ++$levcount; 10300 } 10301 $prevlevel = $chardata[$i]['level']; 10302 } 10303 10304 // W6. Otherwise, separators and terminators change to Other Neutral. 10305 $prevlevel = -1; 10306 $levcount = 0; 10307 for ($i=0; $i < $numchars; ++$i) { 10308 if (($chardata[$i]['type'] == 'ET') OR ($chardata[$i]['type'] == 'ES') OR ($chardata[$i]['type'] == 'CS')) { 10309 $chardata[$i]['type'] = 'ON'; 10310 } 10311 if ($chardata[$i]['level'] != $prevlevel) { 10312 $levcount = 0; 10313 } else { 10314 ++$levcount; 10315 } 10316 $prevlevel = $chardata[$i]['level']; 10317 } 10318 10319 //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. 10320 $prevlevel = -1; 10321 $levcount = 0; 10322 for ($i=0; $i < $numchars; ++$i) { 10323 if ($chardata[$i]['char'] == 'EN') { 10324 for ($j=$levcount; $j >= 0; $j--) { 10325 if ($chardata[$j]['type'] == 'L') { 10326 $chardata[$i]['type'] = 'L'; 10327 } elseif ($chardata[$j]['type'] == 'R') { 10328 break; 10329 } 10330 } 10331 } 10332 if ($chardata[$i]['level'] != $prevlevel) { 10333 $levcount = 0; 10334 } else { 10335 ++$levcount; 10336 } 10337 $prevlevel = $chardata[$i]['level']; 10338 } 10339 10340 // 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. 10341 $prevlevel = -1; 10342 $levcount = 0; 10343 for ($i=0; $i < $numchars; ++$i) { 10344 if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) { 10345 if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) { 10346 $chardata[$i]['type'] = 'L'; 10347 } elseif (($chardata[$i]['type'] == 'N') AND 10348 (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND 10349 (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) { 10350 $chardata[$i]['type'] = 'R'; 10351 } elseif ($chardata[$i]['type'] == 'N') { 10352 // N2. Any remaining neutrals take the embedding direction 10353 $chardata[$i]['type'] = $chardata[$i]['sor']; 10354 } 10355 } elseif (($levcount == 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) { 10356 // first char 10357 if (($chardata[$i]['type'] == 'N') AND ($chardata[$i]['sor'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) { 10358 $chardata[$i]['type'] = 'L'; 10359 } elseif (($chardata[$i]['type'] == 'N') AND 10360 (($chardata[$i]['sor'] == 'R') OR ($chardata[$i]['sor'] == 'EN') OR ($chardata[$i]['sor'] == 'AN')) AND 10361 (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) { 10362 $chardata[$i]['type'] = 'R'; 10363 } elseif ($chardata[$i]['type'] == 'N') { 10364 // N2. Any remaining neutrals take the embedding direction 10365 $chardata[$i]['type'] = $chardata[$i]['sor']; 10366 } 10367 } elseif (($levcount > 0) AND ((($i+1) == $numchars) OR (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] != $prevlevel))) { 10368 //last char 10369 if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[$i]['eor'] == 'L')) { 10370 $chardata[$i]['type'] = 'L'; 10371 } elseif (($chardata[$i]['type'] == 'N') AND 10372 (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND 10373 (($chardata[$i]['eor'] == 'R') OR ($chardata[$i]['eor'] == 'EN') OR ($chardata[$i]['eor'] == 'AN'))) { 10374 $chardata[$i]['type'] = 'R'; 10375 } elseif ($chardata[$i]['type'] == 'N') { 10376 // N2. Any remaining neutrals take the embedding direction 10377 $chardata[$i]['type'] = $chardata[$i]['sor']; 10378 } 10379 } elseif ($chardata[$i]['type'] == 'N') { 10380 // N2. Any remaining neutrals take the embedding direction 10381 $chardata[$i]['type'] = $chardata[$i]['sor']; 10382 } 10383 if ($chardata[$i]['level'] != $prevlevel) { 10384 $levcount = 0; 10385 } else { 10386 ++$levcount; 10387 } 10388 $prevlevel = $chardata[$i]['level']; 10389 } 10390 10391 // 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. 10392 // I2. For all characters with an odd (right-to-left) embedding direction, those of type L, EN or AN go up one level. 10393 for ($i=0; $i < $numchars; ++$i) { 10394 $odd = $chardata[$i]['level'] % 2; 10395 if ($odd) { 10396 if (($chardata[$i]['type'] == 'L') OR ($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')) { 10397 $chardata[$i]['level'] += 1; 10398 } 10399 } else { 10400 if ($chardata[$i]['type'] == 'R') { 10401 $chardata[$i]['level'] += 1; 10402 } elseif (($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')) { 10403 $chardata[$i]['level'] += 2; 10404 } 10405 } 10406 $maxlevel = max($chardata[$i]['level'],$maxlevel); 10407 } 10408 10409 // L1. On each line, reset the embedding level of the following characters to the paragraph embedding level: 10410 // 1. Segment separators, 10411 // 2. Paragraph separators, 10412 // 3. Any sequence of whitespace characters preceding a segment separator or paragraph separator, and 10413 // 4. Any sequence of white space characters at the end of the line. 10414 for ($i=0; $i < $numchars; ++$i) { 10415 if (($chardata[$i]['type'] == 'B') OR ($chardata[$i]['type'] == 'S')) { 10416 $chardata[$i]['level'] = $pel; 10417 } elseif ($chardata[$i]['type'] == 'WS') { 10418 $j = $i+1; 10419 while ($j < $numchars) { 10420 if ((($chardata[$j]['type'] == 'B') OR ($chardata[$j]['type'] == 'S')) OR 10421 (($j == ($numchars-1)) AND ($chardata[$j]['type'] == 'WS'))) { 10422 $chardata[$i]['level'] = $pel; 10423 break; 10424 } elseif ($chardata[$j]['type'] != 'WS') { 10425 break; 10426 } 10427 ++$j; 10428 } 10429 } 10430 } 10431 10432 // Arabic Shaping 10433 // 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. 10434 if ($arabic) { 10435 $endedletter = array(1569,1570,1571,1572,1573,1575,1577,1583,1584,1585,1586,1608,1688); 10436 $alfletter = array(1570,1571,1573,1575); 10437 $chardata2 = $chardata; 10438 $laaletter = false; 10439 $charAL = array(); 10440 $x = 0; 10441 for ($i=0; $i < $numchars; ++$i) { 10442 if (($unicode[$chardata[$i]['char']] == 'AL') OR ($chardata[$i]['char'] == 32) OR ($chardata[$i]['char'] == 8204)) { 10443 $charAL[$x] = $chardata[$i]; 10444 $charAL[$x]['i'] = $i; 10445 $chardata[$i]['x'] = $x; 10446 ++$x; 10447 } 10448 } 10449 $numAL = $x; 10450 for ($i=0; $i < $numchars; ++$i) { 10451 $thischar = $chardata[$i]; 10452 if ($i > 0) { 10453 $prevchar = $chardata[($i-1)]; 10454 } else { 10455 $prevchar = false; 10456 } 10457 if (($i+1) < $numchars) { 10458 $nextchar = $chardata[($i+1)]; 10459 } else { 10460 $nextchar = false; 10461 } 10462 if ($unicode[$thischar['char']] == 'AL') { 10463 $x = $thischar['x']; 10464 if ($x > 0) { 10465 $prevchar = $charAL[($x-1)]; 10466 } else { 10467 $prevchar = false; 10468 } 10469 if (($x+1) < $numAL) { 10470 $nextchar = $charAL[($x+1)]; 10471 } else { 10472 $nextchar = false; 10473 } 10474 // if laa letter 10475 if (($prevchar !== false) AND ($prevchar['char'] == 1604) AND (in_array($thischar['char'], $alfletter))) { 10476 $arabicarr = $laa_array; 10477 $laaletter = true; 10478 if ($x > 1) { 10479 $prevchar = $charAL[($x-2)]; 10480 } else { 10481 $prevchar = false; 10482 } 10483 } else { 10484 $arabicarr = $unicode_arlet; 10485 $laaletter = false; 10486 } 10487 if (($prevchar !== false) AND ($nextchar !== false) AND 10488 (($unicode[$prevchar['char']] == 'AL') OR ($unicode[$prevchar['char']] == 'NSM')) AND 10489 (($unicode[$nextchar['char']] == 'AL') OR ($unicode[$nextchar['char']] == 'NSM')) AND 10490 ($prevchar['type'] == $thischar['type']) AND 10491 ($nextchar['type'] == $thischar['type']) AND 10492 ($nextchar['char'] != 1567)) { 10493 if (in_array($prevchar['char'], $endedletter)) { 10494 if (isset($arabicarr[$thischar['char']][2])) { 10495 // initial 10496 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2]; 10497 } 10498 } else { 10499 if (isset($arabicarr[$thischar['char']][3])) { 10500 // medial 10501 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][3]; 10502 } 10503 } 10504 } elseif (($nextchar !== false) AND 10505 (($unicode[$nextchar['char']] == 'AL') OR ($unicode[$nextchar['char']] == 'NSM')) AND 10506 ($nextchar['type'] == $thischar['type']) AND 10507 ($nextchar['char'] != 1567)) { 10508 if (isset($arabicarr[$chardata[$i]['char']][2])) { 10509 // initial 10510 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2]; 10511 } 10512 } elseif ((($prevchar !== false) AND 10513 (($unicode[$prevchar['char']] == 'AL') OR ($unicode[$prevchar['char']] == 'NSM')) AND 10514 ($prevchar['type'] == $thischar['type'])) OR 10515 (($nextchar !== false) AND ($nextchar['char'] == 1567))) { 10516 // final 10517 if (($i > 1) AND ($thischar['char'] == 1607) AND 10518 ($chardata[$i-1]['char'] == 1604) AND 10519 ($chardata[$i-2]['char'] == 1604)) { 10520 //Allah Word 10521 // mark characters to delete with false 10522 $chardata2[$i-2]['char'] = false; 10523 $chardata2[$i-1]['char'] = false; 10524 $chardata2[$i]['char'] = 65010; 10525 } else { 10526 if (($prevchar !== false) AND in_array($prevchar['char'], $endedletter)) { 10527 if (isset($arabicarr[$thischar['char']][0])) { 10528 // isolated 10529 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0]; 10530 } 10531 } else { 10532 if (isset($arabicarr[$thischar['char']][1])) { 10533 // final 10534 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][1]; 10535 } 10536 } 10537 } 10538 } elseif (isset($arabicarr[$thischar['char']][0])) { 10539 // isolated 10540 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0]; 10541 } 10542 // if laa letter 10543 if ($laaletter) { 10544 // mark characters to delete with false 10545 $chardata2[($charAL[($x-1)]['i'])]['char'] = false; 10546 } 10547 } // end if AL (Arabic Letter) 10548 } // end for each char 10549 /* 10550 * Combining characters that can occur with Arabic Shadda (0651 HEX, 1617 DEC) are replaced. 10551 * Putting the combining mark and shadda in the same glyph allows us to avoid the two marks overlapping each other in an illegible manner. 10552 */ 10553 $cw = &$this->CurrentFont['cw']; 10554 for ($i = 0; $i < ($numchars-1); ++$i) { 10555 if (($chardata2[$i]['char'] == 1617) AND (isset($diacritics[($chardata2[$i+1]['char'])]))) { 10556 // check if the subtitution font is defined on current font 10557 if (isset($cw[($diacritics[($chardata2[$i+1]['char'])])])) { 10558 $chardata2[$i]['char'] = false; 10559 $chardata2[$i+1]['char'] = $diacritics[($chardata2[$i+1]['char'])]; 10560 } 10561 } 10562 } 10563 // remove marked characters 10564 foreach ($chardata2 as $key => $value) { 10565 if ($value['char'] === false) { 10566 unset($chardata2[$key]); 10567 } 10568 } 10569 $chardata = array_values($chardata2); 10570 $numchars = count($chardata); 10571 unset($chardata2); 10572 unset($arabicarr); 10573 unset($laaletter); 10574 unset($charAL); 10575 } 10576 10577 // 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. 10578 for ($j=$maxlevel; $j > 0; $j--) { 10579 $ordarray = Array(); 10580 $revarr = Array(); 10581 $onlevel = false; 10582 for ($i=0; $i < $numchars; ++$i) { 10583 if ($chardata[$i]['level'] >= $j) { 10584 $onlevel = true; 10585 if (isset($unicode_mirror[$chardata[$i]['char']])) { 10586 // 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. 10587 $chardata[$i]['char'] = $unicode_mirror[$chardata[$i]['char']]; 10588 } 10589 $revarr[] = $chardata[$i]; 10590 } else { 10591 if ($onlevel) { 10592 $revarr = array_reverse($revarr); 10593 $ordarray = array_merge($ordarray, $revarr); 10594 $revarr = Array(); 10595 $onlevel = false; 10596 } 10597 $ordarray[] = $chardata[$i]; 10598 } 10599 } 10600 if ($onlevel) { 10601 $revarr = array_reverse($revarr); 10602 $ordarray = array_merge($ordarray, $revarr); 10603 } 10604 $chardata = $ordarray; 10605 } 10606 10607 $ordarray = array(); 10608 for ($i=0; $i < $numchars; ++$i) { 10609 $ordarray[] = $chardata[$i]['char']; 10610 } 10611 10612 return $ordarray; 10613 } 10614 10615 // END OF BIDIRECTIONAL TEXT SECTION ------------------- 10616 10617 /** 10618 * Adds a bookmark. 10619 * @param string $txt bookmark description. 10620 * @param int $level bookmark level (minimum value is 0). 10621 * @param float $y Ordinate of the boorkmark position (default = -1 = current position). 10622 * @param int $page target page number (leave empty for current page). 10623 * @access public 10624 * @author Olivier Plathey, Nicola Asuni 10625 * @since 2.1.002 (2008-02-12) 10626 */ 10627 public function Bookmark($txt, $level=0, $y=-1, $page='') { 10628 if ($level < 0) { 10629 $level = 0; 10630 } 10631 if (isset($this->outlines[0])) { 10632 $lastoutline = end($this->outlines); 10633 $maxlevel = $lastoutline['l'] + 1; 10634 } else { 10635 $maxlevel = 0; 10636 } 10637 if ($level > $maxlevel) { 10638 $level = $maxlevel; 10639 } 10640 if ($y == -1) { 10641 $y = $this->GetY(); 10642 } 10643 if (empty($page)) { 10644 $page = $this->PageNo(); 10645 } 10646 $this->outlines[] = array('t' => $txt, 'l' => $level, 'y' => $y, 'p' => $page); 10647 } 10648 10649 /** 10650 * Create a bookmark PDF string. 10651 * @access protected 10652 * @author Olivier Plathey, Nicola Asuni 10653 * @since 2.1.002 (2008-02-12) 10654 */ 10655 protected function _putbookmarks() { 10656 $nb = count($this->outlines); 10657 if ($nb == 0) { 10658 return; 10659 } 10660 // get sorting columns 10661 $outline_p = array(); 10662 $outline_y = array(); 10663 foreach ($this->outlines as $key => $row) { 10664 $outline_p[$key] = $row['p']; 10665 $outline_k[$key] = $key; 10666 } 10667 // sort outlines by page and original position 10668 array_multisort($outline_p, SORT_NUMERIC, SORT_ASC, $outline_k, SORT_NUMERIC, SORT_ASC, $this->outlines); 10669 $lru = array(); 10670 $level = 0; 10671 foreach ($this->outlines as $i => $o) { 10672 if ($o['l'] > 0) { 10673 $parent = $lru[($o['l'] - 1)]; 10674 //Set parent and last pointers 10675 $this->outlines[$i]['parent'] = $parent; 10676 $this->outlines[$parent]['last'] = $i; 10677 if ($o['l'] > $level) { 10678 //Level increasing: set first pointer 10679 $this->outlines[$parent]['first'] = $i; 10680 } 10681 } else { 10682 $this->outlines[$i]['parent'] = $nb; 10683 } 10684 if (($o['l'] <= $level) AND ($i > 0)) { 10685 //Set prev and next pointers 10686 $prev = $lru[$o['l']]; 10687 $this->outlines[$prev]['next'] = $i; 10688 $this->outlines[$i]['prev'] = $prev; 10689 } 10690 $lru[$o['l']] = $i; 10691 $level = $o['l']; 10692 } 10693 //Outline items 10694 $n = $this->n + 1; 10695 foreach ($this->outlines as $i => $o) { 10696 $this->_newobj(); 10697 // covert HTML title to string 10698 $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'; 10699 $title = preg_replace($nltags, "\n", $o['t']); 10700 $title = preg_replace("/[\r]+/si", '', $title); 10701 $title = preg_replace("/[\n]+/si", "\n", $title); 10702 $title = strip_tags(trim($title)); 10703 $out = '<</Title '.$this->_textstring($title); 10704 $out .= ' /Parent '.($n + $o['parent']).' 0 R'; 10705 if (isset($o['prev'])) { 10706 $out .= ' /Prev '.($n + $o['prev']).' 0 R'; 10707 } 10708 if (isset($o['next'])) { 10709 $out .= ' /Next '.($n + $o['next']).' 0 R'; 10710 } 10711 if (isset($o['first'])) { 10712 $out .= ' /First '.($n + $o['first']).' 0 R'; 10713 } 10714 if (isset($o['last'])) { 10715 $out .= ' /Last '.($n + $o['last']).' 0 R'; 10716 } 10717 $out .= ' '.sprintf('/Dest [%d 0 R /XYZ 0 %.2F null]', (1 + (2 * $o['p'])), ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k))); 10718 $out .= ' /Count 0 >> endobj'; 10719 $this->_out($out); 10720 } 10721 //Outline root 10722 $this->_newobj(); 10723 $this->OutlineRoot = $this->n; 10724 $this->_out('<< /Type /Outlines /First '.$n.' 0 R /Last '.($n + $lru[0]).' 0 R >> endobj'); 10725 } 10726 10727 // --- JAVASCRIPT ------------------------------------------------------ 10728 10729 /** 10730 * Adds a javascript 10731 * @param string $script Javascript code 10732 * @access public 10733 * @author Johannes Güntert, Nicola Asuni 10734 * @since 2.1.002 (2008-02-12) 10735 */ 10736 public function IncludeJS($script) { 10737 $this->javascript .= $script; 10738 } 10739 10740 /** 10741 * Adds a javascript object and return object ID 10742 * @param string $script Javascript code 10743 * @param boolean $onload if true executes this object when opening the document 10744 * @return int internal object ID 10745 * @access public 10746 * @author Nicola Asuni 10747 * @since 4.8.000 (2009-09-07) 10748 */ 10749 public function addJavascriptObject($script, $onload=false) { 10750 ++$this->js_obj_id; 10751 $this->js_objects[$this->js_obj_id] = array('js' => $script, 'onload' => $onload); 10752 return $this->js_obj_id; 10753 } 10754 10755 /** 10756 * Create a javascript PDF string. 10757 * @access protected 10758 * @author Johannes Güntert, Nicola Asuni 10759 * @since 2.1.002 (2008-02-12) 10760 */ 10761 protected function _putjavascript() { 10762 if (empty($this->javascript) AND empty($this->js_objects)) { 10763 return; 10764 } 10765 if (strpos($this->javascript, 'this.addField') > 0) { 10766 if (!$this->ur) { 10767 //$this->setUserRights(); 10768 } 10769 // the following two lines are used to avoid form fields duplication after saving 10770 // The addField method only works on Acrobat Writer, unless the document is signed with Adobe private key (UR3) 10771 $jsa = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%.2F,%.2F,%.2F,%.2F]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1); 10772 $jsb = "getField('tcpdfdocsaved').value='saved';"; 10773 $this->javascript = $jsa."\n".$this->javascript."\n".$jsb; 10774 } 10775 $this->n_js = $this->_newobj(); 10776 $out = ' << /Names ['; 10777 if (!empty($this->javascript)) { 10778 $out .= ' (EmbeddedJS) '.($this->n + 1).' 0 R'; 10779 } 10780 if (!empty($this->js_objects)) { 10781 foreach ($this->js_objects as $key => $val) { 10782 if ($val['onload']) { 10783 $out .= ' (JS'.$key.') '.$key.' 0 R'; 10784 } 10785 } 10786 } 10787 $out .= ' ] >> endobj'; 10788 $this->_out($out); 10789 // default Javascript object 10790 if (!empty($this->javascript)) { 10791 $this->_newobj(); 10792 $out = '<< /S /JavaScript'; 10793 $out .= ' /JS '.$this->_textstring($this->javascript); 10794 $out .= ' >> endobj'; 10795 $this->_out($out); 10796 } 10797 // additional Javascript objects 10798 if (!empty($this->js_objects)) { 10799 foreach ($this->js_objects as $key => $val) { 10800 $this->offsets[$key] = $this->bufferlen; 10801 $out = $key.' 0 obj'."\n".' << /S /JavaScript /JS '.$this->_textstring($val['js']).' >> endobj'; 10802 $this->_out($out); 10803 } 10804 } 10805 } 10806 10807 /** 10808 * Convert color to javascript color. 10809 * @param string $color color name or #RRGGBB 10810 * @access protected 10811 * @author Denis Van Nuffelen, Nicola Asuni 10812 * @since 2.1.002 (2008-02-12) 10813 */ 10814 protected function _JScolor($color) { 10815 static $aColors = array('transparent', 'black', 'white', 'red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'dkGray', 'gray', 'ltGray'); 10816 if (substr($color,0,1) == '#') { 10817 return sprintf("['RGB',%.3F,%.3F,%.3F]", hexdec(substr($color,1,2))/255, hexdec(substr($color,3,2))/255, hexdec(substr($color,5,2))/255); 10818 } 10819 if (!in_array($color,$aColors)) { 10820 $this->Error('Invalid color: '.$color); 10821 } 10822 return 'color.'.$color; 10823 } 10824 10825 /** 10826 * Adds a javascript form field. 10827 * @param string $type field type 10828 * @param string $name field name 10829 * @param int $x horizontal position 10830 * @param int $y vertical position 10831 * @param int $w width 10832 * @param int $h height 10833 * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 10834 * @access protected 10835 * @author Denis Van Nuffelen, Nicola Asuni 10836 * @since 2.1.002 (2008-02-12) 10837 */ 10838 protected function _addfield($type, $name, $x, $y, $w, $h, $prop) { 10839 if ($this->rtl) { 10840 $x = $x - $w; 10841 } 10842 // the followind avoid fields duplication after saving the document 10843 $this->javascript .= "if(getField('tcpdfdocsaved').value != 'saved') {"; 10844 $k = $this->k; 10845 $this->javascript .= sprintf("f".$name."=this.addField('%s','%s',%d,[%.2F,%.2F,%.2F,%.2F]);", $name, $type, $this->PageNo()-1, $x*$k, ($this->h-$y)*$k+1, ($x+$w)*$k, ($this->h-$y-$h)*$k+1)."\n"; 10846 $this->javascript .= 'f'.$name.'.textSize='.$this->FontSizePt.";\n"; 10847 while (list($key, $val) = each($prop)) { 10848 if (strcmp(substr($key, -5), 'Color') == 0) { 10849 $val = $this->_JScolor($val); 10850 } else { 10851 $val = "'".$val."'"; 10852 } 10853 $this->javascript .= 'f'.$name.'.'.$key.'='.$val.";\n"; 10854 } 10855 if ($this->rtl) { 10856 $this->x -= $w; 10857 } else { 10858 $this->x += $w; 10859 } 10860 $this->javascript .= '}'; 10861 } 10862 10863 // --- FORM FIELDS ----------------------------------------------------- 10864 10865 /** 10866 * Convert JavaScript form fields properties array to Annotation Properties array. 10867 * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 10868 * @return array of annotation properties 10869 * @access protected 10870 * @author Nicola Asuni 10871 * @since 4.8.000 (2009-09-06) 10872 */ 10873 protected function getAnnotOptFromJSProp($prop) { 10874 if (isset($prop['aopt']) AND is_array($prop['aopt'])) { 10875 // the annotation options area lready defined 10876 return $prop['aopt']; 10877 } 10878 $opt = array(); // value to be returned 10879 // alignment: Controls how the text is laid out within the text field. 10880 if (isset($prop['alignment'])) { 10881 switch ($prop['alignment']) { 10882 case 'left': { 10883 $opt['q'] = 0; 10884 break; 10885 } 10886 case 'center': { 10887 $opt['q'] = 1; 10888 break; 10889 } 10890 case 'right': { 10891 $opt['q'] = 2; 10892 break; 10893 } 10894 default: { 10895 $opt['q'] = ($this->rtl)?2:0; 10896 break; 10897 } 10898 } 10899 } 10900 // lineWidth: Specifies the thickness of the border when stroking the perimeter of a field's rectangle. 10901 if (isset($prop['lineWidth'])) { 10902 $linewidth = intval($prop['lineWidth']); 10903 } else { 10904 $linewidth = 1; 10905 } 10906 // borderStyle: The border style for a field. 10907 if (isset($prop['borderStyle'])) { 10908 switch ($prop['borderStyle']) { 10909 case 'border.d': 10910 case 'dashed': { 10911 $opt['border'] = array(0, 0, $linewidth, array(3, 2)); 10912 $opt['bs'] = array('w'=>$linewidth, 's'=>'D', 'd'=>array(3, 2)); 10913 break; 10914 } 10915 case 'border.b': 10916 case 'beveled': { 10917 $opt['border'] = array(0, 0, $linewidth); 10918 $opt['bs'] = array('w'=>$linewidth, 's'=>'B'); 10919 break; 10920 } 10921 case 'border.i': 10922 case 'inset': { 10923 $opt['border'] = array(0, 0, $linewidth); 10924 $opt['bs'] = array('w'=>$linewidth, 's'=>'I'); 10925 break; 10926 } 10927 case 'border.u': 10928 case 'underline': { 10929 $opt['border'] = array(0, 0, $linewidth); 10930 $opt['bs'] = array('w'=>$linewidth, 's'=>'U'); 10931 break; 10932 } 10933 default: 10934 case 'border.s': 10935 case 'solid': { 10936 $opt['border'] = array(0, 0, $linewidth); 10937 $opt['bs'] = array('w'=>$linewidth, 's'=>'S'); 10938 break; 10939 } 10940 } 10941 } 10942 if (isset($prop['border']) AND is_array($prop['border'])) { 10943 $opt['border'] = $prop['border']; 10944 } 10945 if (!isset($opt['mk'])) { 10946 $opt['mk'] = array(); 10947 } 10948 if (!isset($opt['mk']['if'])) { 10949 $opt['mk']['if'] = array(); 10950 } 10951 $opt['mk']['if']['a'] = array(0.5, 0.5); 10952 // buttonAlignX: Controls how space is distributed from the left of the button face with respect to the icon. 10953 if (isset($prop['buttonAlignX'])) { 10954 $opt['mk']['if']['a'][0] = $prop['buttonAlignX']; 10955 } 10956 // buttonAlignY: Controls how unused space is distributed from the bottom of the button face with respect to the icon. 10957 if (isset($prop['buttonAlignY'])) { 10958 $opt['mk']['if']['a'][1] = $prop['buttonAlignY']; 10959 } 10960 // buttonFitBounds: If true, the extent to which the icon may be scaled is set to the bounds of the button field. 10961 if (isset($prop['buttonFitBounds']) AND ($prop['buttonFitBounds'] == 'true')) { 10962 $opt['mk']['if']['fb'] = true; 10963 } 10964 // buttonScaleHow: Controls how the icon is scaled (if necessary) to fit inside the button face. 10965 if (isset($prop['buttonScaleHow'])) { 10966 switch ($prop['buttonScaleHow']) { 10967 case 'scaleHow.proportional': { 10968 $opt['mk']['if']['s'] = 'P'; 10969 break; 10970 } 10971 case 'scaleHow.anamorphic': { 10972 $opt['mk']['if']['s'] = 'A'; 10973 break; 10974 } 10975 } 10976 } 10977 // buttonScaleWhen: Controls when an icon is scaled to fit inside the button face. 10978 if (isset($prop['buttonScaleWhen'])) { 10979 switch ($prop['buttonScaleWhen']) { 10980 case 'scaleWhen.always': { 10981 $opt['mk']['if']['sw'] = 'A'; 10982 break; 10983 } 10984 case 'scaleWhen.never': { 10985 $opt['mk']['if']['sw'] = 'N'; 10986 break; 10987 } 10988 case 'scaleWhen.tooBig': { 10989 $opt['mk']['if']['sw'] = 'B'; 10990 break; 10991 } 10992 case 'scaleWhen.tooSmall': { 10993 $opt['mk']['if']['sw'] = 'S'; 10994 break; 10995 } 10996 } 10997 } 10998 // buttonPosition: Controls how the text and the icon of the button are positioned with respect to each other within the button face. 10999 if (isset($prop['buttonPosition'])) { 11000 switch ($prop['buttonPosition']) { 11001 case 0: 11002 case 'position.textOnly': { 11003 $opt['mk']['tp'] = 0; 11004 break; 11005 } 11006 case 1: 11007 case 'position.iconOnly': { 11008 $opt['mk']['tp'] = 1; 11009 break; 11010 } 11011 case 2: 11012 case 'position.iconTextV': { 11013 $opt['mk']['tp'] = 2; 11014 break; 11015 } 11016 case 3: 11017 case 'position.textIconV': { 11018 $opt['mk']['tp'] = 3; 11019 break; 11020 } 11021 case 4: 11022 case 'position.iconTextH': { 11023 $opt['mk']['tp'] = 4; 11024 break; 11025 } 11026 case 5: 11027 case 'position.textIconH': { 11028 $opt['mk']['tp'] = 5; 11029 break; 11030 } 11031 case 6: 11032 case 'position.overlay': { 11033 $opt['mk']['tp'] = 6; 11034 break; 11035 } 11036 } 11037 } 11038 // fillColor: Specifies the background color for a field. 11039 if (isset($prop['fillColor'])) { 11040 if (is_array($prop['fillColor'])) { 11041 $opt['mk']['bg'] = $prop['fillColor']; 11042 } else { 11043 $opt['mk']['bg'] = $this->convertHTMLColorToDec($prop['fillColor']); 11044 } 11045 } 11046 // 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. 11047 if (isset($prop['strokeColor'])) { 11048 if (is_array($prop['strokeColor'])) { 11049 $opt['mk']['bc'] = $prop['strokeColor']; 11050 } else { 11051 $opt['mk']['bc'] = $this->convertHTMLColorToDec($prop['strokeColor']); 11052 } 11053 } 11054 // rotation: The rotation of a widget in counterclockwise increments. 11055 if (isset($prop['rotation'])) { 11056 $opt['mk']['r'] = $prop['rotation']; 11057 } 11058 // charLimit: Limits the number of characters that a user can type into a text field. 11059 if (isset($prop['charLimit'])) { 11060 $opt['maxlen'] = intval($prop['charLimit']); 11061 } 11062 if (!isset($ff)) { 11063 $ff = 0; 11064 } 11065 // readonly: The read-only characteristic of a field. If a field is read-only, the user can see the field but cannot change it. 11066 if (isset($prop['readonly']) AND ($prop['readonly'] == 'true')) { 11067 $ff += 1 << 0; 11068 } 11069 // required: Specifies whether a field requires a value. 11070 if (isset($prop['required']) AND ($prop['required'] == 'true')) { 11071 $ff += 1 << 1; 11072 } 11073 // multiline: Controls how text is wrapped within the field. 11074 if (isset($prop['multiline']) AND ($prop['multiline'] == 'true')) { 11075 $ff += 1 << 12; 11076 } 11077 // password: Specifies whether the field should display asterisks when data is entered in the field. 11078 if (isset($prop['password']) AND ($prop['password'] == 'true')) { 11079 $ff += 1 << 13; 11080 } 11081 // NoToggleToOff: If set, exactly one radio button shall be selected at all times; selecting the currently selected button has no effect. 11082 if (isset($prop['NoToggleToOff']) AND ($prop['NoToggleToOff'] == 'true')) { 11083 $ff += 1 << 14; 11084 } 11085 // Radio: If set, the field is a set of radio buttons. 11086 if (isset($prop['Radio']) AND ($prop['Radio'] == 'true')) { 11087 $ff += 1 << 15; 11088 } 11089 // Pushbutton: If set, the field is a pushbutton that does not retain a permanent value. 11090 if (isset($prop['Pushbutton']) AND ($prop['Pushbutton'] == 'true')) { 11091 $ff += 1 << 16; 11092 } 11093 // Combo: If set, the field is a combo box; if clear, the field is a list box. 11094 if (isset($prop['Combo']) AND ($prop['Combo'] == 'true')) { 11095 $ff += 1 << 17; 11096 } 11097 // editable: Controls whether a combo box is editable. 11098 if (isset($prop['editable']) AND ($prop['editable'] == 'true')) { 11099 $ff += 1 << 18; 11100 } 11101 // Sort: If set, the field's option items shall be sorted alphabetically. 11102 if (isset($prop['Sort']) AND ($prop['Sort'] == 'true')) { 11103 $ff += 1 << 19; 11104 } 11105 // fileSelect: If true, sets the file-select flag in the Options tab of the text field (Field is Used for File Selection). 11106 if (isset($prop['fileSelect']) AND ($prop['fileSelect'] == 'true')) { 11107 $ff += 1 << 20; 11108 } 11109 // multipleSelection: If true, indicates that a list box allows a multiple selection of items. 11110 if (isset($prop['multipleSelection']) AND ($prop['multipleSelection'] == 'true')) { 11111 $ff += 1 << 21; 11112 } 11113 // doNotSpellCheck: If true, spell checking is not performed on this editable text field. 11114 if (isset($prop['doNotSpellCheck']) AND ($prop['doNotSpellCheck'] == 'true')) { 11115 $ff += 1 << 22; 11116 } 11117 // doNotScroll: If true, the text field does not scroll and the user, therefore, is limited by the rectangular region designed for the field. 11118 if (isset($prop['doNotScroll']) AND ($prop['doNotScroll'] == 'true')) { 11119 $ff += 1 << 23; 11120 } 11121 // 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. 11122 if (isset($prop['comb']) AND ($prop['comb'] == 'true')) { 11123 $ff += 1 << 24; 11124 } 11125 // 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. 11126 if (isset($prop['radiosInUnison']) AND ($prop['radiosInUnison'] == 'true')) { 11127 $ff += 1 << 25; 11128 } 11129 // richText: If true, the field allows rich text formatting. 11130 if (isset($prop['richText']) AND ($prop['richText'] == 'true')) { 11131 $ff += 1 << 25; 11132 } 11133 // commitOnSelChange: Controls whether a field value is committed after a selection change. 11134 if (isset($prop['commitOnSelChange']) AND ($prop['commitOnSelChange'] == 'true')) { 11135 $ff += 1 << 26; 11136 } 11137 $opt['ff'] = $ff; 11138 // defaultValue: The default value of a field - that is, the value that the field is set to when the form is reset. 11139 if (isset($prop['defaultValue'])) { 11140 $opt['dv'] = $prop['defaultValue']; 11141 } 11142 $f = 4; // default value for annotation flags 11143 // readonly: The read-only characteristic of a field. If a field is read-only, the user can see the field but cannot change it. 11144 if (isset($prop['readonly']) AND ($prop['readonly'] == 'true')) { 11145 $f += 1 << 6; 11146 } 11147 // display: Controls whether the field is hidden or visible on screen and in print. 11148 if (isset($prop['display'])) { 11149 if ($prop['display'] == 'display.visible') { 11150 // 11151 } elseif ($prop['display'] == 'display.hidden') { 11152 $f += 1 << 1; 11153 } elseif ($prop['display'] == 'display.noPrint') { 11154 $f -= 1 << 2; 11155 } elseif ($prop['display'] == 'display.noView') { 11156 $f += 1 << 5; 11157 } 11158 } 11159 $opt['f'] = $f; 11160 // currentValueIndices: Reads and writes single or multiple values of a list box or combo box. 11161 if (isset($prop['currentValueIndices']) AND is_array($prop['currentValueIndices'])) { 11162 $opt['i'] = $prop['currentValueIndices']; 11163 } 11164 // value: The value of the field data that the user has entered. 11165 if (isset($prop['value'])) { 11166 if (is_array($prop['value'])) { 11167 $opt['opt'] = array(); 11168 foreach ($prop['value'] AS $key => $optval) { 11169 // exportValues: An array of strings representing the export values for the field. 11170 if (isset($prop['exportValues'][$key])) { 11171 $opt['opt'][$key] = array($prop['exportValues'][$key], $prop['value'][$key]); 11172 } else { 11173 $opt['opt'][$key] = $prop['value'][$key]; 11174 } 11175 } 11176 } else { 11177 $opt['v'] = $prop['value']; 11178 } 11179 } 11180 // richValue: This property specifies the text contents and formatting of a rich text field. 11181 if (isset($prop['richValue'])) { 11182 $opt['rv'] = $prop['richValue']; 11183 } 11184 // submitName: If nonempty, used during form submission instead of name. Only applicable if submitting in HTML format (that is, URL-encoded). 11185 if (isset($prop['submitName'])) { 11186 $opt['tm'] = $prop['submitName']; 11187 } 11188 // name: Fully qualified field name. 11189 if (isset($prop['name'])) { 11190 $opt['t'] = $prop['name']; 11191 } 11192 // userName: The user name (short description string) of the field. 11193 if (isset($prop['userName'])) { 11194 $opt['tu'] = $prop['userName']; 11195 } 11196 // highlight: Defines how a button reacts when a user clicks it. 11197 if (isset($prop['highlight'])) { 11198 switch ($prop['highlight']) { 11199 case 'none': 11200 case 'highlight.n': { 11201 $opt['h'] = 'N'; 11202 break; 11203 } 11204 case 'invert': 11205 case 'highlight.i': { 11206 $opt['h'] = 'i'; 11207 break; 11208 } 11209 case 'push': 11210 case 'highlight.p': { 11211 $opt['h'] = 'P'; 11212 break; 11213 } 11214 case 'outline': 11215 case 'highlight.o': { 11216 $opt['h'] = 'O'; 11217 break; 11218 } 11219 } 11220 } 11221 // Unsupported options: 11222 // - calcOrderIndex: Changes the calculation order of fields in the document. 11223 // - delay: Delays the redrawing of a field's appearance. 11224 // - defaultStyle: This property defines the default style attributes for the form field. 11225 // - style: Allows the user to set the glyph style of a check box or radio button. 11226 // - textColor, textFont, textSize 11227 return $opt; 11228 } 11229 11230 /** 11231 * Set default properties for form fields. 11232 * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 11233 * @access public 11234 * @author Nicola Asuni 11235 * @since 4.8.000 (2009-09-06) 11236 */ 11237 public function setFormDefaultProp($prop=array()) { 11238 $this->default_form_prop = $prop; 11239 } 11240 11241 /** 11242 * Return the default properties for form fields. 11243 * @return array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 11244 * @access public 11245 * @author Nicola Asuni 11246 * @since 4.8.000 (2009-09-06) 11247 */ 11248 public function getFormDefaultProp() { 11249 return $this->default_form_prop; 11250 } 11251 11252 /** 11253 * Creates a text field 11254 * @param string $name field name 11255 * @param float $w Width of the rectangle 11256 * @param float $h Height of the rectangle 11257 * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 11258 * @param array $opt annotation parameters. Possible values are described on official PDF32000_2008 reference. 11259 * @param float $x Abscissa of the upper-left corner of the rectangle 11260 * @param float $y Ordinate of the upper-left corner of the rectangle 11261 * @param boolean $js if true put the field using JavaScript (requires Acrobat Writer to be rendered). 11262 * @access public 11263 * @author Nicola Asuni 11264 * @since 4.8.000 (2009-09-07) 11265 */ 11266 public function TextField($name, $w, $h, $prop=array(), $opt=array(), $x='', $y='', $js=false) { 11267 if ($x === '') { 11268 $x = $this->x; 11269 } 11270 if ($y === '') { 11271 $y = $this->y; 11272 } 11273 if ($js) { 11274 $this->_addfield('text', $name, $x, $y, $w, $h, $prop); 11275 return; 11276 } 11277 // get default style 11278 $prop = array_merge($this->getFormDefaultProp(), $prop); 11279 // get annotation data 11280 $popt = $this->getAnnotOptFromJSProp($prop); 11281 // set default appearance stream 11282 $font = $this->FontFamily; 11283 $fontkey = array_search($font, $this->fontkeys); 11284 if (!in_array($fontkey, $this->annotation_fonts)) { 11285 $this->annotation_fonts[$font] = $fontkey; 11286 } 11287 $fontstyle = sprintf('/F%d %.2F Tf %s', ($fontkey + 1), $this->FontSizePt, $this->TextColor); 11288 $popt['da'] = $fontstyle; 11289 $popt['ap'] = array(); 11290 $popt['ap']['n'] = 'q BT '.$fontstyle.' ET Q'; 11291 // merge options 11292 $opt = array_merge($popt, $opt); 11293 // remove some conflicting options 11294 unset($opt['bs']); 11295 // set remaining annotation data 11296 $opt['Subtype'] = 'Widget'; 11297 $opt['ft'] = 'Tx'; 11298 $opt['t'] = $name; 11299 /* 11300 Additional annotation's parameters (check _putannotsobj() method): 11301 //$opt['f'] 11302 //$opt['ap'] 11303 //$opt['as'] 11304 //$opt['bs'] 11305 //$opt['be'] 11306 //$opt['c'] 11307 //$opt['border'] 11308 //$opt['h'] 11309 //$opt['mk'] 11310 //$opt['mk']['r'] 11311 //$opt['mk']['bc'] 11312 //$opt['mk']['bg'] 11313 //$opt['mk']['ca'] 11314 //$opt['mk']['rc'] 11315 //$opt['mk']['ac'] 11316 //$opt['mk']['i'] 11317 //$opt['mk']['ri'] 11318 //$opt['mk']['ix'] 11319 //$opt['mk']['if'] 11320 //$opt['mk']['if']['sw'] 11321 //$opt['mk']['if']['s'] 11322 //$opt['mk']['if']['a'] 11323 //$opt['mk']['if']['fb'] 11324 //$opt['mk']['tp'] 11325 //$opt['tu'] 11326 //$opt['tm'] 11327 //$opt['ff'] 11328 //$opt['v'] 11329 //$opt['dv'] 11330 //$opt['a'] 11331 //$opt['aa'] 11332 //$opt['q'] 11333 */ 11334 $this->Annotation($x, $y, $w, $h, $name, $opt, 0); 11335 if ($this->rtl) { 11336 $this->x -= $w; 11337 } else { 11338 $this->x += $w; 11339 } 11340 } 11341 11342 /** 11343 * Creates a RadioButton field 11344 * @param string $name field name 11345 * @param int $w width 11346 * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 11347 * @param array $opt annotation parameters. Possible values are described on official PDF32000_2008 reference. 11348 * @param string $onvalue value to be returned if selected. 11349 * @param boolean $checked define the initial state. 11350 * @param float $x Abscissa of the upper-left corner of the rectangle 11351 * @param float $y Ordinate of the upper-left corner of the rectangle 11352 * @param boolean $js if true put the field using JavaScript (requires Acrobat Writer to be rendered). 11353 * @access public 11354 * @author Nicola Asuni 11355 * @since 4.8.000 (2009-09-07) 11356 */ 11357 public function RadioButton($name, $w, $prop=array(), $opt=array(), $onvalue='On', $checked=false, $x='', $y='', $js=false) { 11358 if ($x === '') { 11359 $x = $this->x; 11360 } 11361 if ($y === '') { 11362 $y = $this->y; 11363 } 11364 if ($js) { 11365 $this->_addfield('radiobutton', $name, $x, $y, $w, $w, $prop); 11366 return; 11367 } 11368 if ($this->empty_string($onvalue)) { 11369 $onvalue = 'On'; 11370 } 11371 if ($checked) { 11372 $defval = $onvalue; 11373 } else { 11374 $defval = 'Off'; 11375 } 11376 // set data for parent group 11377 if (!isset($this->radiobutton_groups[$this->page])) { 11378 $this->radiobutton_groups[$this->page] = array(); 11379 } 11380 if (!isset($this->radiobutton_groups[$this->page][$name])) { 11381 $this->radiobutton_groups[$this->page][$name] = array(); 11382 ++$this->annot_obj_id; 11383 $this->radio_groups[] = $this->annot_obj_id; 11384 } 11385 // save object ID to be added on Kids entry on parent object 11386 $this->radiobutton_groups[$this->page][$name][] = array('kid' => ($this->annot_obj_id + 1), 'def' => $defval); 11387 // get default style 11388 $prop = array_merge($this->getFormDefaultProp(), $prop); 11389 $prop['NoToggleToOff'] = 'true'; 11390 $prop['Radio'] = 'true'; 11391 $prop['borderStyle'] = 'inset'; 11392 // get annotation data 11393 $popt = $this->getAnnotOptFromJSProp($prop); 11394 // set additional default values 11395 $font = 'zapfdingbats'; 11396 $this->AddFont($font); 11397 $fontkey = array_search($font, $this->fontkeys); 11398 if (!in_array($fontkey, $this->annotation_fonts)) { 11399 $this->annotation_fonts[$font] = $fontkey; 11400 } 11401 $fontstyle = sprintf('/F%d %.2F Tf %s', ($fontkey + 1), $this->FontSizePt, $this->TextColor); 11402 $popt['da'] = $fontstyle; 11403 $popt['ap'] = array(); 11404 $popt['ap']['n'] = array(); 11405 $popt['ap']['n'][$onvalue] = 'q BT '.$fontstyle.' 0 0 Td (8) Tj ET Q'; 11406 $popt['ap']['n']['Off'] = 'q BT '.$fontstyle.' 0 0 Td (8) Tj ET Q'; 11407 if (!isset($popt['mk'])) { 11408 $popt['mk'] = array(); 11409 } 11410 $popt['mk']['ca'] = '(l)'; 11411 // merge options 11412 $opt = array_merge($popt, $opt); 11413 // set remaining annotation data 11414 $opt['Subtype'] = 'Widget'; 11415 $opt['ft'] = 'Btn'; 11416 if ($checked) { 11417 $opt['v'] = array('/'.$onvalue); 11418 $opt['as'] = $onvalue; 11419 } else { 11420 $opt['as'] = 'Off'; 11421 } 11422 $this->Annotation($x, $y, $w, $w, $name, $opt, 0); 11423 if ($this->rtl) { 11424 $this->x -= $w; 11425 } else { 11426 $this->x += $w; 11427 } 11428 } 11429 11430 /** 11431 * Creates a List-box field 11432 * @param string $name field name 11433 * @param int $w width 11434 * @param int $h height 11435 * @param array $values array containing the list of values. 11436 * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 11437 * @param array $opt annotation parameters. Possible values are described on official PDF32000_2008 reference. 11438 * @param float $x Abscissa of the upper-left corner of the rectangle 11439 * @param float $y Ordinate of the upper-left corner of the rectangle 11440 * @param boolean $js if true put the field using JavaScript (requires Acrobat Writer to be rendered). 11441 * @access public 11442 * @author Nicola Asuni 11443 * @since 4.8.000 (2009-09-07) 11444 */ 11445 public function ListBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) { 11446 if ($x === '') { 11447 $x = $this->x; 11448 } 11449 if ($y === '') { 11450 $y = $this->y; 11451 } 11452 if ($js) { 11453 $this->_addfield('listbox', $name, $x, $y, $w, $h, $prop); 11454 $s = ''; 11455 foreach ($values as $value) { 11456 $s .= "'".addslashes($value)."',"; 11457 } 11458 $this->javascript .= 'f'.$name.'.setItems(['.substr($s, 0, -1)."]);\n"; 11459 return; 11460 } 11461 // get default style 11462 $prop = array_merge($this->getFormDefaultProp(), $prop); 11463 // get annotation data 11464 $popt = $this->getAnnotOptFromJSProp($prop); 11465 // set additional default values 11466 $font = $this->FontFamily; 11467 $fontkey = array_search($font, $this->fontkeys); 11468 if (!in_array($fontkey, $this->annotation_fonts)) { 11469 $this->annotation_fonts[$font] = $fontkey; 11470 } 11471 $fontstyle = sprintf('/F%d %.2F Tf %s', ($fontkey + 1), $this->FontSizePt, $this->TextColor); 11472 $popt['da'] = $fontstyle; 11473 $popt['ap'] = array(); 11474 $popt['ap']['n'] = 'q BT '.$fontstyle.' ET Q'; 11475 // merge options 11476 $opt = array_merge($popt, $opt); 11477 // set remaining annotation data 11478 $opt['Subtype'] = 'Widget'; 11479 $opt['ft'] = 'Ch'; 11480 $opt['t'] = $name; 11481 $opt['opt'] = $values; 11482 $this->Annotation($x, $y, $w, $h, $name, $opt, 0); 11483 if ($this->rtl) { 11484 $this->x -= $w; 11485 } else { 11486 $this->x += $w; 11487 } 11488 } 11489 11490 /** 11491 * Creates a Combo-box field 11492 * @param string $name field name 11493 * @param int $w width 11494 * @param int $h height 11495 * @param array $values array containing the list of values. 11496 * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 11497 * @param array $opt annotation parameters. Possible values are described on official PDF32000_2008 reference. 11498 * @param float $x Abscissa of the upper-left corner of the rectangle 11499 * @param float $y Ordinate of the upper-left corner of the rectangle 11500 * @param boolean $js if true put the field using JavaScript (requires Acrobat Writer to be rendered). 11501 * @access public 11502 * @author Nicola Asuni 11503 * @since 4.8.000 (2009-09-07) 11504 */ 11505 public function ComboBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) { 11506 if ($x === '') { 11507 $x = $this->x; 11508 } 11509 if ($y === '') { 11510 $y = $this->y; 11511 } 11512 if ($js) { 11513 $this->_addfield('combobox', $name, $x, $y, $w, $h, $prop); 11514 $s = ''; 11515 foreach ($values as $value) { 11516 $s .= "'".addslashes($value)."',"; 11517 } 11518 $this->javascript .= 'f'.$name.'.setItems(['.substr($s, 0, -1)."]);\n"; 11519 return; 11520 } 11521 // get default style 11522 $prop = array_merge($this->getFormDefaultProp(), $prop); 11523 $prop['Combo'] = true; 11524 // get annotation data 11525 $popt = $this->getAnnotOptFromJSProp($prop); 11526 // set additional default options 11527 $font = $this->FontFamily; 11528 $fontkey = array_search($font, $this->fontkeys); 11529 if (!in_array($fontkey, $this->annotation_fonts)) { 11530 $this->annotation_fonts[$font] = $fontkey; 11531 } 11532 $fontstyle = sprintf('/F%d %.2F Tf %s', ($fontkey + 1), $this->FontSizePt, $this->TextColor); 11533 $popt['da'] = $fontstyle; 11534 $popt['ap'] = array(); 11535 $popt['ap']['n'] = 'q BT '.$fontstyle.' ET Q'; 11536 // merge options 11537 $opt = array_merge($popt, $opt); 11538 // set remaining annotation data 11539 $opt['Subtype'] = 'Widget'; 11540 $opt['ft'] = 'Ch'; 11541 $opt['t'] = $name; 11542 $opt['opt'] = $values; 11543 $this->Annotation($x, $y, $w, $h, $name, $opt, 0); 11544 if ($this->rtl) { 11545 $this->x -= $w; 11546 } else { 11547 $this->x += $w; 11548 } 11549 } 11550 11551 /** 11552 * Creates a CheckBox field 11553 * @param string $name field name 11554 * @param int $w width 11555 * @param boolean $checked define the initial state. 11556 * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 11557 * @param array $opt annotation parameters. Possible values are described on official PDF32000_2008 reference. 11558 * @param string $onvalue value to be returned if selected. 11559 * @param float $x Abscissa of the upper-left corner of the rectangle 11560 * @param float $y Ordinate of the upper-left corner of the rectangle 11561 * @param boolean $js if true put the field using JavaScript (requires Acrobat Writer to be rendered). 11562 * @access public 11563 * @author Nicola Asuni 11564 * @since 4.8.000 (2009-09-07) 11565 */ 11566 public function CheckBox($name, $w, $checked=false, $prop=array(), $opt=array(), $onvalue='Yes', $x='', $y='', $js=false) { 11567 if ($x === '') { 11568 $x = $this->x; 11569 } 11570 if ($y === '') { 11571 $y = $this->y; 11572 } 11573 if ($js) { 11574 $this->_addfield('checkbox', $name, $x, $y, $w, $w, $prop); 11575 return; 11576 } 11577 if (!isset($prop['value'])) { 11578 $prop['value'] = array('Yes'); 11579 } 11580 // get default style 11581 $prop = array_merge($this->getFormDefaultProp(), $prop); 11582 $prop['borderStyle'] = 'inset'; 11583 // get annotation data 11584 $popt = $this->getAnnotOptFromJSProp($prop); 11585 // set additional default options 11586 $font = 'zapfdingbats'; 11587 $this->AddFont($font); 11588 $fontkey = array_search($font, $this->fontkeys); 11589 if (!in_array($fontkey, $this->annotation_fonts)) { 11590 $this->annotation_fonts[$font] = $fontkey; 11591 } 11592 $fontstyle = sprintf('/F%d %.2F Tf %s', ($fontkey + 1), $this->FontSizePt, $this->TextColor); 11593 $popt['da'] = $fontstyle; 11594 $popt['ap'] = array(); 11595 $popt['ap']['n'] = array(); 11596 $popt['ap']['n']['Yes'] = 'q BT '.$fontstyle.' 0 0 Td (8) Tj ET Q'; 11597 $popt['ap']['n']['Off'] = 'q BT '.$fontstyle.' 0 0 Td (8) Tj ET Q'; 11598 // merge options 11599 $opt = array_merge($popt, $opt); 11600 // set remaining annotation data 11601 $opt['Subtype'] = 'Widget'; 11602 $opt['ft'] = 'Btn'; 11603 $opt['t'] = $name; 11604 $opt['opt'] = array($onvalue); 11605 if ($checked) { 11606 $opt['v'] = array('/0'); 11607 $opt['as'] = 'Yes'; 11608 } else { 11609 $opt['v'] = array('/Off'); 11610 $opt['as'] = 'Off'; 11611 } 11612 $this->Annotation($x, $y, $w, $w, $name, $opt, 0); 11613 if ($this->rtl) { 11614 $this->x -= $w; 11615 } else { 11616 $this->x += $w; 11617 } 11618 } 11619 11620 /** 11621 * Creates a button field 11622 * @param string $name field name 11623 * @param int $w width 11624 * @param int $h height 11625 * @param string $caption caption. 11626 * @param mixed $action action triggered by pressing the button. Use a string to specify a javascript action. Use an array to specify a form action options as on section 12.7.5 of PDF32000_2008. 11627 * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference. 11628 * @param array $opt annotation parameters. Possible values are described on official PDF32000_2008 reference. 11629 * @param float $x Abscissa of the upper-left corner of the rectangle 11630 * @param float $y Ordinate of the upper-left corner of the rectangle 11631 * @param boolean $js if true put the field using JavaScript (requires Acrobat Writer to be rendered). 11632 * @access public 11633 * @author Nicola Asuni 11634 * @since 4.8.000 (2009-09-07) 11635 */ 11636 public function Button($name, $w, $h, $caption, $action, $prop=array(), $opt=array(), $x='', $y='', $js=false) { 11637 if ($x === '') { 11638 $x = $this->x; 11639 } 11640 if ($y === '') { 11641 $y = $this->y; 11642 } 11643 if ($js) { 11644 $this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop); 11645 $this->javascript .= 'f'.$name.".buttonSetCaption('".addslashes($caption)."');\n"; 11646 $this->javascript .= 'f'.$name.".setAction('MouseUp','".addslashes($action)."');\n"; 11647 $this->javascript .= 'f'.$name.".highlight='push';\n"; 11648 $this->javascript .= 'f'.$name.".print=false;\n"; 11649 return; 11650 } 11651 // get default style 11652 $prop = array_merge($this->getFormDefaultProp(), $prop); 11653 $prop['Pushbutton'] = 'true'; 11654 $prop['highlight'] = 'push'; 11655 $prop['display'] = 'display.noPrint'; 11656 // get annotation data 11657 $popt = $this->getAnnotOptFromJSProp($prop); 11658 // set additional default options 11659 if (!isset($popt['mk'])) { 11660 $popt['mk'] = array(); 11661 } 11662 $popt['mk']['ca'] = $this->_textstring($caption); 11663 $popt['mk']['rc'] = $this->_textstring($caption); 11664 $popt['mk']['ac'] = $this->_textstring($caption); 11665 $font = $this->FontFamily; 11666 $fontkey = array_search($font, $this->fontkeys); 11667 if (!in_array($fontkey, $this->annotation_fonts)) { 11668 $this->annotation_fonts[$font] = $fontkey; 11669 } 11670 $fontstyle = sprintf('/F%d %.2F Tf %s', ($fontkey + 1), $this->FontSizePt, $this->TextColor); 11671 $popt['da'] = $fontstyle; 11672 $popt['ap'] = array(); 11673 $popt['ap']['n'] = 'q BT '.$fontstyle.' ET Q'; 11674 // merge options 11675 $opt = array_merge($popt, $opt); 11676 // set remaining annotation data 11677 $opt['Subtype'] = 'Widget'; 11678 $opt['ft'] = 'Btn'; 11679 $opt['t'] = $caption; 11680 $opt['v'] = $name; 11681 if (!empty($action)) { 11682 if (is_array($action)) { 11683 // form action options as on section 12.7.5 of PDF32000_2008. 11684 $opt['aa'] = '/D <<'; 11685 $bmode = array('SubmitForm', 'ResetForm', 'ImportData'); 11686 foreach ($action AS $key => $val) { 11687 if (($key == 'S') AND in_array($val, $bmode)) { 11688 $opt['aa'] .= ' /S /'.$val; 11689 } elseif (($key == 'F') AND (!empty($val))) { 11690 $opt['aa'] .= ' /F '.$this->_datastring($val); 11691 } elseif (($key == 'Fields') AND is_array($val) AND !empty($val)) { 11692 $opt['aa'] .= ' /Fields ['; 11693 foreach ($val AS $field) { 11694 $opt['aa'] .= ' '.$this->_textstring($field); 11695 } 11696 $opt['aa'] .= ']'; 11697 } elseif (($key == 'Flags')) { 11698 $ff = 0; 11699 if (is_array($val)) { 11700 foreach ($val AS $flag) { 11701 switch ($flag) { 11702 case 'Include/Exclude': { 11703 $ff += 1 << 0; 11704 break; 11705 } 11706 case 'IncludeNoValueFields': { 11707 $ff += 1 << 1; 11708 break; 11709 } 11710 case 'ExportFormat': { 11711 $ff += 1 << 2; 11712 break; 11713 } 11714 case 'GetMethod': { 11715 $ff += 1 << 3; 11716 break; 11717 } 11718 case 'SubmitCoordinates': { 11719 $ff += 1 << 4; 11720 break; 11721 } 11722 case 'XFDF': { 11723 $ff += 1 << 5; 11724 break; 11725 } 11726 case 'IncludeAppendSaves': { 11727 $ff += 1 << 6; 11728 break; 11729 } 11730 case 'IncludeAnnotations': { 11731 $ff += 1 << 7; 11732 break; 11733 } 11734 case 'SubmitPDF': { 11735 $ff += 1 << 8; 11736 break; 11737 } 11738 case 'CanonicalFormat': { 11739 $ff += 1 << 9; 11740 break; 11741 } 11742 case 'ExclNonUserAnnots': { 11743 $ff += 1 << 10; 11744 break; 11745 } 11746 case 'ExclFKey': { 11747 $ff += 1 << 11; 11748 break; 11749 } 11750 case 'EmbedForm': { 11751 $ff += 1 << 13; 11752 break; 11753 } 11754 } 11755 } 11756 } else { 11757 $ff = intval($val); 11758 } 11759 $opt['aa'] .= ' /Flags '.$ff; 11760 } 11761 } 11762 $opt['aa'] .= ' >>'; 11763 } else { 11764 // Javascript action or raw action command 11765 $js_obj_id = $this->addJavascriptObject($action); 11766 $opt['aa'] = '/D '.$js_obj_id.' 0 R'; 11767 } 11768 } 11769 $this->Annotation($x, $y, $w, $h, $name, $opt, 0); 11770 if ($this->rtl) { 11771 $this->x -= $w; 11772 } else { 11773 $this->x += $w; 11774 } 11775 } 11776 11777 // --- END FORMS FIELDS ------------------------------------------------ 11778 11779 /** 11780 * Add certification signature (DocMDP or UR3) 11781 * You can set only one signature type 11782 * @access protected 11783 * @author Nicola Asuni 11784 * @since 4.6.008 (2009-05-07) 11785 */ 11786 protected function _putsignature() { 11787 if ((!$this->sign) OR (!isset($this->signature_data['cert_type']))) { 11788 return; 11789 } 11790 $this->_newobj(); 11791 $out = ' << /Type /Sig'; 11792 $out .= ' /Filter /Adobe.PPKLite'; 11793 $out .= ' /SubFilter /adbe.pkcs7.detached'; 11794 $out .= ' '.$this->byterange_string; 11795 $out .= ' /Contents<>'.str_repeat(' ', $this->signature_max_length); 11796 $out .= ' /Reference'; 11797 $out .= ' ['; 11798 $out .= ' << /Type /SigRef'; 11799 if ($this->signature_data['cert_type'] > 0) { 11800 $out .= ' /TransformMethod /DocMDP'; 11801 $out .= ' /TransformParams'; 11802 $out .= ' <<'; 11803 $out .= ' /Type /TransformParams'; 11804 $out .= ' /V /1.2'; 11805 $out .= ' /P '.$this->signature_data['cert_type']; 11806 } else { 11807 $out .= ' /TransformMethod /UR3'; 11808 $out .= ' /TransformParams'; 11809 $out .= ' << /Type /TransformParams'; 11810 $out .= ' /V /2.2'; 11811 if (!$this->empty_string($this->ur_document)) { 11812 $out .= ' /Document['.$this->ur_document.']'; 11813 } 11814 if (!$this->empty_string($this->ur_annots)) { 11815 $out .= ' /Annots['.$this->ur_annots.']'; 11816 } 11817 if (!$this->empty_string($this->ur_form)) { 11818 $out .= ' /Form['.$this->ur_form.']'; 11819 } 11820 if (!$this->empty_string($this->ur_signature)) { 11821 $out .= ' /Signature['.$this->ur_signature.']'; 11822 } 11823 } 11824 $out .= ' >> >> ]'; 11825 if (isset($this->signature_data['info']['Name']) AND !$this->empty_string($this->signature_data['info']['Name'])) { 11826 $out .= ' /Name '.$this->_textstring($this->signature_data['info']['Name']); 11827 } 11828 if (isset($this->signature_data['info']['Location']) AND !$this->empty_string($this->signature_data['info']['Location'])) { 11829 $out .= ' /Location '.$this->_textstring($this->signature_data['info']['Location']); 11830 } 11831 if (isset($this->signature_data['info']['Reason']) AND !$this->empty_string($this->signature_data['info']['Reason'])) { 11832 $out .= ' /Reason '.$this->_textstring($this->signature_data['info']['Reason']); 11833 } 11834 if (isset($this->signature_data['info']['ContactInfo']) AND !$this->empty_string($this->signature_data['info']['ContactInfo'])) { 11835 $out .= ' /ContactInfo '.$this->_textstring($this->signature_data['info']['ContactInfo']); 11836 } 11837 $out .= ' /M '.$this->_datestring(); 11838 $out .= ' >> endobj'; 11839 $this->_out($out); 11840 } 11841 11842 /** 11843 * Set User's Rights for PDF Reader 11844 * WARNING: This works only using the Adobe private key with the setSignature() method!. 11845 * Check the PDF Reference 8.7.1 Transform Methods, 11846 * Table 8.105 Entries in the UR transform parameters dictionary 11847 * @param boolean $enable if true enable user's rights on PDF reader 11848 * @param string $document Names specifying additional document-wide usage rights for the document. The only defined value is "/FullSave", which permits a user to save the document along with modified form and/or annotation data. 11849 * @param string $annots Names specifying additional annotation-related usage rights for the document. Valid names in PDF 1.5 and later are /Create/Delete/Modify/Copy/Import/Export, which permit the user to perform the named operation on annotations. 11850 * @param string $form Names specifying additional form-field-related usage rights for the document. Valid names are: /Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate 11851 * @param string $signature Names specifying additional signature-related usage rights for the document. The only defined value is /Modify, which permits a user to apply a digital signature to an existing signature form field or clear a signed signature form field. 11852 * @access public 11853 * @author Nicola Asuni 11854 * @since 2.9.000 (2008-03-26) 11855 */ 11856 public function setUserRights( 11857 $enable=true, 11858 $document='/FullSave', 11859 $annots='/Create/Delete/Modify/Copy/Import/Export', 11860 $form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate', 11861 $signature='/Modify') { 11862 $this->ur = $enable; 11863 $this->ur_document = $document; 11864 $this->ur_annots = $annots; 11865 $this->ur_form = $form; 11866 $this->ur_signature = $signature; 11867 if (!$this->sign) { 11868 // This signature only works using the Adobe Private key that is unavailable! 11869 $this->setSignature('', '', '', '', 0, array()); 11870 } 11871 } 11872 11873 /** 11874 * Enable document signature (requires the OpenSSL Library). 11875 * The digital signature improve document authenticity and integrity and allows o enable extra features on Acrobat Reader. 11876 * @param mixed $signing_cert signing certificate (string or filename prefixed with 'file://') 11877 * @param mixed $private_key private key (string or filename prefixed with 'file://') 11878 * @param string $private_key_password password 11879 * @param string $extracerts specifies the name of a file containing a bunch of extra certificates to include in the signature which can for example be used to help the recipient to verify the certificate that you used. 11880 * @param int $cert_type The access permissions granted for this document. Valid values shall be: 1 = No changes to the document shall be permitted; any change to the document shall invalidate the signature; 2 = Permitted changes shall be filling in forms, instantiating page templates, and signing; other changes shall invalidate the signature; 3 = Permitted changes shall be the same as for 2, as well as annotation creation, deletion, and modification; other changes shall invalidate the signature. 11881 * @param array $info array of option information: Name, Location, Reason, ContactInfo. 11882 * @access public 11883 * @author Nicola Asuni 11884 * @since 4.6.005 (2009-04-24) 11885 */ 11886 public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array()) { 11887 // to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt 11888 // to convert pfx certificate to pem: openssl 11889 // OpenSSL> pkcs12 -in <cert.pfx> -out <cert.crt> -nodes 11890 $this->sign = true; 11891 $this->signature_data = array(); 11892 if (strlen($signing_cert) == 0) { 11893 $signing_cert = 'file://'.dirname(__FILE__).'/tcpdf.crt'; 11894 $private_key_password = 'tcpdfdemo'; 11895 } 11896 if (strlen($private_key) == 0) { 11897 $private_key = $signing_cert; 11898 } 11899 $this->signature_data['signcert'] = $signing_cert; 11900 $this->signature_data['privkey'] = $private_key; 11901 $this->signature_data['password'] = $private_key_password; 11902 $this->signature_data['extracerts'] = $extracerts; 11903 $this->signature_data['cert_type'] = $cert_type; 11904 $this->signature_data['info'] = $info; 11905 } 11906 11907 /** 11908 * Create a new page group. 11909 * NOTE: call this function before calling AddPage() 11910 * @param int $page starting group page (leave empty for next page). 11911 * @access public 11912 * @since 3.0.000 (2008-03-27) 11913 */ 11914 public function startPageGroup($page='') { 11915 if (empty($page)) { 11916 $page = $this->page + 1; 11917 } 11918 $this->newpagegroup[$page] = true; 11919 } 11920 11921 /** 11922 * Defines an alias for the total number of pages. 11923 * It will be substituted as the document is closed. 11924 * @param string $alias The alias. 11925 * @access public 11926 * @since 1.4 11927 * @see getAliasNbPages(), PageNo(), Footer() 11928 */ 11929 public function AliasNbPages($alias='{nb}') { 11930 $this->AliasNbPages = $alias; 11931 } 11932 11933 /** 11934 * Returns the string alias used for the total number of pages. 11935 * If the current font is unicode type, the returned string is surrounded by additional curly braces. 11936 * @return string 11937 * @access public 11938 * @since 4.0.018 (2008-08-08) 11939 * @see AliasNbPages(), PageNo(), Footer() 11940 */ 11941 public function getAliasNbPages() { 11942 if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) { 11943 return '{'.$this->AliasNbPages.'}'; 11944 } 11945 return $this->AliasNbPages; 11946 } 11947 11948 /** 11949 * Defines an alias for the page number. 11950 * It will be substituted as the document is closed. 11951 * @param string $alias The alias. 11952 * @access public 11953 * @since 4.5.000 (2009-01-02) 11954 * @see getAliasNbPages(), PageNo(), Footer() 11955 */ 11956 public function AliasNumPage($alias='{pnb}') { 11957 //Define an alias for total number of pages 11958 $this->AliasNumPage = $alias; 11959 } 11960 11961 /** 11962 * Returns the string alias used for the page number. 11963 * If the current font is unicode type, the returned string is surrounded by additional curly braces. 11964 * @return string 11965 * @access public 11966 * @since 4.5.000 (2009-01-02) 11967 * @see AliasNbPages(), PageNo(), Footer() 11968 */ 11969 public function getAliasNumPage() { 11970 if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) { 11971 return '{'.$this->AliasNumPage.'}'; 11972 } 11973 return $this->AliasNumPage; 11974 } 11975 11976 /** 11977 * Return the current page in the group. 11978 * @return current page in the group 11979 * @access public 11980 * @since 3.0.000 (2008-03-27) 11981 */ 11982 public function getGroupPageNo() { 11983 return $this->pagegroups[$this->currpagegroup]; 11984 } 11985 11986 /** 11987 * Returns the current group page number formatted as a string. 11988 * @access public 11989 * @since 4.3.003 (2008-11-18) 11990 * @see PaneNo(), formatPageNumber() 11991 */ 11992 public function getGroupPageNoFormatted() { 11993 return $this->formatPageNumber($this->getGroupPageNo()); 11994 } 11995 11996 /** 11997 * Return the alias of the current page group 11998 * If the current font is unicode type, the returned string is surrounded by additional curly braces. 11999 * (will be replaced by the total number of pages in this group). 12000 * @return alias of the current page group 12001 * @access public 12002 * @since 3.0.000 (2008-03-27) 12003 */ 12004 public function getPageGroupAlias() { 12005 if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) { 12006 return '{'.$this->currpagegroup.'}'; 12007 } 12008 return $this->currpagegroup; 12009 } 12010 12011 /** 12012 * Return the alias for the page number on the current page group 12013 * If the current font is unicode type, the returned string is surrounded by additional curly braces. 12014 * (will be replaced by the total number of pages in this group). 12015 * @return alias of the current page group 12016 * @access public 12017 * @since 4.5.000 (2009-01-02) 12018 */ 12019 public function getPageNumGroupAlias() { 12020 if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) { 12021 return '{'.str_replace('{nb', '{pnb', $this->currpagegroup).'}'; 12022 } 12023 return str_replace('{nb', '{pnb', $this->currpagegroup); 12024 } 12025 12026 /** 12027 * Format the page numbers. 12028 * This method can be overriden for custom formats. 12029 * @param int $num page number 12030 * @access protected 12031 * @since 4.2.005 (2008-11-06) 12032 */ 12033 protected function formatPageNumber($num) { 12034 return number_format((float)$num, 0, '', '.'); 12035 } 12036 12037 /** 12038 * Format the page numbers on the Table Of Content. 12039 * This method can be overriden for custom formats. 12040 * @param int $num page number 12041 * @access protected 12042 * @since 4.5.001 (2009-01-04) 12043 * @see addTOC(), addHTMLTOC() 12044 */ 12045 protected function formatTOCPageNumber($num) { 12046 return number_format((float)$num, 0, '', '.'); 12047 } 12048 12049 /** 12050 * Returns the current page number formatted as a string. 12051 * @access public 12052 * @since 4.2.005 (2008-11-06) 12053 * @see PaneNo(), formatPageNumber() 12054 */ 12055 public function PageNoFormatted() { 12056 return $this->formatPageNumber($this->PageNo()); 12057 } 12058 12059 /** 12060 * Put visibility settings. 12061 * @access protected 12062 * @since 3.0.000 (2008-03-27) 12063 */ 12064 protected function _putocg() { 12065 $this->_newobj(); 12066 $this->n_ocg_print = $this->n; 12067 $this->_out('<< /Type /OCG /Name '.$this->_textstring('print').' /Usage << /Print <</PrintState /ON>> /View <</ViewState /OFF>> >> >> endobj'); 12068 $this->_newobj(); 12069 $this->n_ocg_view = $this->n; 12070 $this->_out('<< /Type /OCG /Name '.$this->_textstring('view').' /Usage << /Print <</PrintState /OFF>> /View <</ViewState /ON>> >> >> endobj'); 12071 } 12072 12073 /** 12074 * Set the visibility of the successive elements. 12075 * This can be useful, for instance, to put a background 12076 * image or color that will show on screen but won't print. 12077 * @param string $v visibility mode. Legal values are: all, print, screen. 12078 * @access public 12079 * @since 3.0.000 (2008-03-27) 12080 */ 12081 public function setVisibility($v) { 12082 if ($this->openMarkedContent) { 12083 // close existing open marked-content 12084 $this->_out('EMC'); 12085 $this->openMarkedContent = false; 12086 } 12087 switch($v) { 12088 case 'print': { 12089 $this->_out('/OC /OC1 BDC'); 12090 $this->openMarkedContent = true; 12091 break; 12092 } 12093 case 'screen': { 12094 $this->_out('/OC /OC2 BDC'); 12095 $this->openMarkedContent = true; 12096 break; 12097 } 12098 case 'all': { 12099 $this->_out(''); 12100 break; 12101 } 12102 default: { 12103 $this->Error('Incorrect visibility: '.$v); 12104 break; 12105 } 12106 } 12107 $this->visibility = $v; 12108 } 12109 12110 /** 12111 * Add transparency parameters to the current extgstate 12112 * @param array $params parameters 12113 * @return the number of extgstates 12114 * @access protected 12115 * @since 3.0.000 (2008-03-27) 12116 */ 12117 protected function addExtGState($parms) { 12118 $n = count($this->extgstates) + 1; 12119 // check if this ExtGState already exist 12120 for ($i = 1; $i < $n; ++$i) { 12121 if ($this->extgstates[$i]['parms'] == $parms) { 12122 // return reference to existing ExtGState 12123 return $i; 12124 } 12125 } 12126 $this->extgstates[$n]['parms'] = $parms; 12127 return $n; 12128 } 12129 12130 /** 12131 * Add an extgstate 12132 * @param array $gs extgstate 12133 * @access protected 12134 * @since 3.0.000 (2008-03-27) 12135 */ 12136 protected function setExtGState($gs) { 12137 $this->_out(sprintf('/GS%d gs', $gs)); 12138 } 12139 12140 /** 12141 * Put extgstates for object transparency 12142 * @param array $gs extgstate 12143 * @access protected 12144 * @since 3.0.000 (2008-03-27) 12145 */ 12146 protected function _putextgstates() { 12147 $ne = count($this->extgstates); 12148 for ($i = 1; $i <= $ne; ++$i) { 12149 $this->_newobj(); 12150 $this->extgstates[$i]['n'] = $this->n; 12151 $out = '<< /Type /ExtGState'; 12152 foreach ($this->extgstates[$i]['parms'] as $k => $v) { 12153 if (is_float($v)) { 12154 $v = sprintf('%.2F', $v); 12155 } 12156 $out .= ' /'.$k.' '.$v; 12157 } 12158 $out .= ' >> endobj'; 12159 $this->_out($out); 12160 } 12161 } 12162 12163 /** 12164 * Set alpha for stroking (CA) and non-stroking (ca) operations. 12165 * @param float $alpha real value from 0 (transparent) to 1 (opaque) 12166 * @param string $bm blend mode, one of the following: Normal, Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn, HardLight, SoftLight, Difference, Exclusion, Hue, Saturation, Color, Luminosity 12167 * @access public 12168 * @since 3.0.000 (2008-03-27) 12169 */ 12170 public function setAlpha($alpha, $bm='Normal') { 12171 $gs = $this->addExtGState(array('ca' => $alpha, 'CA' => $alpha, 'BM' => '/'.$bm, 'AIS' => 'false')); 12172 $this->setExtGState($gs); 12173 } 12174 12175 /** 12176 * Set the default JPEG compression quality (1-100) 12177 * @param int $quality JPEG quality, integer between 1 and 100 12178 * @access public 12179 * @since 3.0.000 (2008-03-27) 12180 */ 12181 public function setJPEGQuality($quality) { 12182 if (($quality < 1) OR ($quality > 100)) { 12183 $quality = 75; 12184 } 12185 $this->jpeg_quality = intval($quality); 12186 } 12187 12188 /** 12189 * Set the default number of columns in a row for HTML tables. 12190 * @param int $cols number of columns 12191 * @access public 12192 * @since 3.0.014 (2008-06-04) 12193 */ 12194 public function setDefaultTableColumns($cols=4) { 12195 $this->default_table_columns = intval($cols); 12196 } 12197 12198 /** 12199 * Set the height of the cell (line height) respect the font height. 12200 * @param int $h cell proportion respect font height (typical value = 1.25). 12201 * @access public 12202 * @since 3.0.014 (2008-06-04) 12203 */ 12204 public function setCellHeightRatio($h) { 12205 $this->cell_height_ratio = $h; 12206 } 12207 12208 /** 12209 * return the height of cell repect font height. 12210 * @access public 12211 * @since 4.0.012 (2008-07-24) 12212 */ 12213 public function getCellHeightRatio() { 12214 return $this->cell_height_ratio; 12215 } 12216 12217 /** 12218 * Set the PDF version (check PDF reference for valid values). 12219 * Default value is 1.t 12220 * @access public 12221 * @since 3.1.000 (2008-06-09) 12222 */ 12223 public function setPDFVersion($version='1.7') { 12224 $this->PDFVersion = $version; 12225 } 12226 12227 /** 12228 * Set the viewer preferences dictionary controlling the way the document is to be presented on the screen or in print. 12229 * (see Section 8.1 of PDF reference, "Viewer Preferences"). 12230 * <ul><li>HideToolbar boolean (Optional) A flag specifying whether to hide the viewer application's tool bars when the document is active. Default value: false.</li><li>HideMenubar boolean (Optional) A flag specifying whether to hide the viewer application's menu bar when the document is active. Default value: false.</li><li>HideWindowUI boolean (Optional) A flag specifying whether to hide user interface elements in the document's window (such as scroll bars and navigation controls), leaving only the document's contents displayed. Default value: false.</li><li>FitWindow boolean (Optional) A flag specifying whether to resize the document's window to fit the size of the first displayed page. Default value: false.</li><li>CenterWindow boolean (Optional) A flag specifying whether to position the document's window in the center of the screen. Default value: false.</li><li>DisplayDocTitle boolean (Optional; PDF 1.4) A flag specifying whether the window's title bar should display the document title taken from the Title entry of the document information dictionary (see Section 10.2.1, "Document Information Dictionary"). If false, the title bar should instead display the name of the PDF file containing the document. Default value: false.</li><li>NonFullScreenPageMode name (Optional) The document's page mode, specifying how to display the document on exiting full-screen mode:<ul><li>UseNone Neither document outline nor thumbnail images visible</li><li>UseOutlines Document outline visible</li><li>UseThumbs Thumbnail images visible</li><li>UseOC Optional content group panel visible</li></ul>This entry is meaningful only if the value of the PageMode entry in the catalog dictionary (see Section 3.6.1, "Document Catalog") is FullScreen; it is ignored otherwise. Default value: UseNone.</li><li>ViewArea name (Optional; PDF 1.4) The name of the page boundary representing the area of a page to be displayed when viewing the document on the screen. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li><li>ViewClip name (Optional; PDF 1.4) The name of the page boundary to which the contents of a page are to be clipped when viewing the document on the screen. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li><li>PrintArea name (Optional; PDF 1.4) The name of the page boundary representing the area of a page to be rendered when printing the document. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li><li>PrintClip name (Optional; PDF 1.4) The name of the page boundary to which the contents of a page are to be clipped when printing the document. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li><li>PrintScaling name (Optional; PDF 1.6) The page scaling option to be selected when a print dialog is displayed for this document. Valid values are: <ul><li>None, which indicates that the print dialog should reflect no page scaling</li><li>AppDefault (default), which indicates that applications should use the current print scaling</li></ul></li><li>Duplex name (Optional; PDF 1.7) The paper handling option to use when printing the file from the print dialog. The following values are valid:<ul><li>Simplex - Print single-sided</li><li>DuplexFlipShortEdge - Duplex and flip on the short edge of the sheet</li><li>DuplexFlipLongEdge - Duplex and flip on the long edge of the sheet</li></ul>Default value: none</li><li>PickTrayByPDFSize boolean (Optional; PDF 1.7) A flag specifying whether the PDF page size is used to select the input paper tray. This setting influences only the preset values used to populate the print dialog presented by a PDF viewer application. If PickTrayByPDFSize is true, the check box in the print dialog associated with input paper tray is checked. Note: This setting has no effect on Mac OS systems, which do not provide the ability to pick the input tray by size.</li><li>PrintPageRange array (Optional; PDF 1.7) The page numbers used to initialize the print dialog box when the file is printed. The first page of the PDF file is denoted by 1. Each pair consists of the first and last pages in the sub-range. An odd number of integers causes this entry to be ignored. Negative numbers cause the entire array to be ignored. Default value: as defined by PDF viewer application</li><li>NumCopies integer (Optional; PDF 1.7) The number of copies to be printed when the print dialog is opened for this file. Supported values are the integers 2 through 5. Values outside this range are ignored. Default value: as defined by PDF viewer application, but typically 1</li></ul> 12231 * @param array $preferences array of options. 12232 * @author Nicola Asuni 12233 * @access public 12234 * @since 3.1.000 (2008-06-09) 12235 */ 12236 public function setViewerPreferences($preferences) { 12237 $this->viewer_preferences = $preferences; 12238 } 12239 12240 /** 12241 * Paints color transition registration bars 12242 * @param float $x abscissa of the top left corner of the rectangle. 12243 * @param float $y ordinate of the top left corner of the rectangle. 12244 * @param float $w width of the rectangle. 12245 * @param float $h height of the rectangle. 12246 * @param boolean $transition if true prints tcolor transitions to white. 12247 * @param boolean $vertical if true prints bar vertically. 12248 * @param string $colors colors to print, one letter per color separated by comma (for example 'A,W,R,G,B,C,M,Y,K'): A=black, W=white, R=red, G=green, B=blue, C=cyan, M=magenta, Y=yellow, K=black. 12249 * @author Nicola Asuni 12250 * @since 4.9.000 (2010-03-26) 12251 * @access public 12252 */ 12253 public function colorRegistrationBar($x, $y, $w, $h, $transition=true, $vertical=false, $colors='A,R,G,B,C,M,Y,K') { 12254 $bars = explode(',', $colors); 12255 $numbars = count($bars); // number of bars to print 12256 // set bar measures 12257 if ($vertical) { 12258 $coords = array(0, 0, 0, 1); 12259 $wb = $w / $numbars; // bar width 12260 $hb = $h; // bar height 12261 $xd = $wb; // delta x 12262 $yd = 0; // delta y 12263 } else { 12264 $coords = array(1, 0, 0, 0); 12265 $wb = $w; // bar width 12266 $hb = $h / $numbars; // bar height 12267 $xd = 0; // delta x 12268 $yd = $hb; // delta y 12269 } 12270 $xb = $x; 12271 $yb = $y; 12272 foreach ($bars as $col) { 12273 switch ($col) { 12274 // set transition colors 12275 case 'A': { // BLACK 12276 $col_a = array(255); 12277 $col_b = array(0); 12278 break; 12279 } 12280 case 'W': { // WHITE 12281 $col_a = array(0); 12282 $col_b = array(255); 12283 break; 12284 } 12285 case 'R': { // R 12286 $col_a = array(255,255,255); 12287 $col_b = array(255,0,0); 12288 break; 12289 } 12290 case 'G': { // G 12291 $col_a = array(255,255,255); 12292 $col_b = array(0,255,0); 12293 break; 12294 } 12295 case 'B': { // B 12296 $col_a = array(255,255,255); 12297 $col_b = array(0,0,255); 12298 break; 12299 } 12300 case 'C': { // C 12301 $col_a = array(0,0,0,0); 12302 $col_b = array(100,0,0,0); 12303 break; 12304 } 12305 case 'M': { // M 12306 $col_a = array(0,0,0,0); 12307 $col_b = array(0,100,0,0); 12308 break; 12309 } 12310 case 'Y': { // Y 12311 $col_a = array(0,0,0,0); 12312 $col_b = array(0,0,100,0); 12313 break; 12314 } 12315 case 'K': { // K 12316 $col_a = array(0,0,0,0); 12317 $col_b = array(0,0,0,100); 12318 break; 12319 } 12320 default: { // GRAY 12321 $col_a = array(255); 12322 $col_b = array(0); 12323 break; 12324 } 12325 } 12326 if ($transition) { 12327 // color gradient 12328 $this->LinearGradient($xb, $yb, $wb, $hb, $col_a, $col_b, $coords); 12329 } else { 12330 // color rectangle 12331 $this->SetFillColorArray($col_b); 12332 $this->Rect($xb, $yb, $wb, $hb, 'F', array()); 12333 } 12334 $xb += $xd; 12335 $yb += $yd; 12336 } 12337 } 12338 12339 /** 12340 * Paints crop mark 12341 * @param float $x abscissa of the crop mark center. 12342 * @param float $y ordinate of the crop mark center. 12343 * @param float $w width of the crop mark. 12344 * @param float $h height of the crop mark. 12345 * @param string $type type of crop mark, one sybol per type separated by comma: A = top left, B = top right, C = bottom left, D = bottom right. 12346 * @param array $color crop mark color (default black). 12347 * @author Nicola Asuni 12348 * @since 4.9.000 (2010-03-26) 12349 * @access public 12350 */ 12351 public function cropMark($x, $y, $w, $h, $type='A,B,C,D', $color=array(0,0,0)) { 12352 $this->SetLineStyle(array('width' => (0.5 / $this->k), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $color)); 12353 $crops = explode(',', $type); 12354 $numcrops = count($crops); // number of crop marks to print 12355 $dw = $w / 4; // horizontal space to leave before the intersection point 12356 $dh = $h / 4; // vertical space to leave before the intersection point 12357 foreach ($crops as $crop) { 12358 switch ($crop) { 12359 case 'A': { 12360 $x1 = $x; 12361 $y1 = $y - $h; 12362 $x2 = $x; 12363 $y2 = $y - $dh; 12364 $x3 = $x - $w; 12365 $y3 = $y; 12366 $x4 = $x - $dw; 12367 $y4 = $y; 12368 break; 12369 } 12370 case 'B': { 12371 $x1 = $x; 12372 $y1 = $y - $h; 12373 $x2 = $x; 12374 $y2 = $y - $dh; 12375 $x3 = $x + $dw; 12376 $y3 = $y; 12377 $x4 = $x + $w; 12378 $y4 = $y; 12379 break; 12380 } 12381 case 'C': { 12382 $x1 = $x - $w; 12383 $y1 = $y; 12384 $x2 = $x - $dw; 12385 $y2 = $y; 12386 $x3 = $x; 12387 $y3 = $y + $dh; 12388 $x4 = $x; 12389 $y4 = $y + $h; 12390 break; 12391 } 12392 case 'D': { 12393 $x1 = $x + $dw; 12394 $y1 = $y; 12395 $x2 = $x + $w; 12396 $y2 = $y; 12397 $x3 = $x; 12398 $y3 = $y + $dh; 12399 $x4 = $x; 12400 $y4 = $y + $h; 12401 break; 12402 } 12403 } 12404 $this->Line($x1, $y1, $x2, $y2); 12405 $this->Line($x3, $y3, $x4, $y4); 12406 } 12407 } 12408 12409 /** 12410 * Paints a registration mark 12411 * @param float $x abscissa of the registration mark center. 12412 * @param float $y ordinate of the registration mark center. 12413 * @param float $r radius of the crop mark. 12414 * @param boolean $double if true print two concentric crop marks. 12415 * @param array $cola crop mark color (default black). 12416 * @param array $colb second crop mark color. 12417 * @author Nicola Asuni 12418 * @since 4.9.000 (2010-03-26) 12419 * @access public 12420 */ 12421 public function registrationMark($x, $y, $r, $double=false, $cola=array(0,0,0), $colb=array(255,255,255)) { 12422 $line_style = array('width' => (0.5 / $this->k), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $cola); 12423 $this->SetFillColorArray($cola); 12424 $this->PieSector($x, $y, $r, 90, 180, 'F'); 12425 $this->PieSector($x, $y, $r, 270, 360, 'F'); 12426 $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8); 12427 if ($double) { 12428 $r2 = $r * 0.5; 12429 $this->SetFillColorArray($colb); 12430 $this->PieSector($x, $y, $r2, 90, 180, 'F'); 12431 $this->PieSector($x, $y, $r2, 270, 360, 'F'); 12432 $this->SetFillColorArray($cola); 12433 $this->PieSector($x, $y, $r2, 0, 90, 'F'); 12434 $this->PieSector($x, $y, $r2, 180, 270, 'F'); 12435 $this->Circle($x, $y, $r2, 0, 360, 'C', $line_style, array(), 8); 12436 } 12437 } 12438 12439 /** 12440 * Paints a linear colour gradient. 12441 * @param float $x abscissa of the top left corner of the rectangle. 12442 * @param float $y ordinate of the top left corner of the rectangle. 12443 * @param float $w width of the rectangle. 12444 * @param float $h height of the rectangle. 12445 * @param array $col1 first color (Grayscale, RGB or CMYK components). 12446 * @param array $col2 second color (Grayscale, RGB or CMYK components). 12447 * @param array $coords array of the form (x1, y1, x2, y2) which defines the gradient vector (see linear_gradient_coords.jpg). The default value is from left to right (x1=0, y1=0, x2=1, y2=0). 12448 * @author Andreas Würmser, Nicola Asuni 12449 * @since 3.1.000 (2008-06-09) 12450 * @access public 12451 */ 12452 public function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) { 12453 $this->Clip($x, $y, $w, $h); 12454 $this->Gradient(2, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false); 12455 } 12456 12457 /** 12458 * Paints a radial colour gradient. 12459 * @param float $x abscissa of the top left corner of the rectangle. 12460 * @param float $y ordinate of the top left corner of the rectangle. 12461 * @param float $w width of the rectangle. 12462 * @param float $h height of the rectangle. 12463 * @param array $col1 first color (Grayscale, RGB or CMYK components). 12464 * @param array $col2 second color (Grayscale, RGB or CMYK components). 12465 * @param array $coords array of the form (fx, fy, cx, cy, r) where (fx, fy) is the starting point of the gradient with color1, (cx, cy) is the center of the circle with color2, and r is the radius of the circle (see radial_gradient_coords.jpg). (fx, fy) should be inside the circle, otherwise some areas will not be defined. 12466 * @author Andreas Würmser, Nicola Asuni 12467 * @since 3.1.000 (2008-06-09) 12468 * @access public 12469 */ 12470 public function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) { 12471 $this->Clip($x, $y, $w, $h); 12472 $this->Gradient(3, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false); 12473 } 12474 12475 /** 12476 * Paints a coons patch mesh. 12477 * @param float $x abscissa of the top left corner of the rectangle. 12478 * @param float $y ordinate of the top left corner of the rectangle. 12479 * @param float $w width of the rectangle. 12480 * @param float $h height of the rectangle. 12481 * @param array $col1 first color (lower left corner) (RGB components). 12482 * @param array $col2 second color (lower right corner) (RGB components). 12483 * @param array $col3 third color (upper right corner) (RGB components). 12484 * @param array $col4 fourth color (upper left corner) (RGB components). 12485 * @param array $coords <ul><li>for one patch mesh: array(float x1, float y1, .... float x12, float y12): 12 pairs of coordinates (normally from 0 to 1) which specify the Bezier control points that define the patch. First pair is the lower left edge point, next is its right control point (control point 2). Then the other points are defined in the order: control point 1, edge point, control point 2 going counter-clockwise around the patch. Last (x12, y12) is the first edge point's left control point (control point 1).</li><li>for two or more patch meshes: array[number of patches]: arrays with the following keys for each patch: f: where to put that patch (0 = first patch, 1, 2, 3 = right, top and left of precedent patch - I didn't figure this out completely - just try and error ;-) points: 12 pairs of coordinates of the Bezier control points as above for the first patch, 8 pairs of coordinates for the following patches, ignoring the coordinates already defined by the precedent patch (I also didn't figure out the order of these - also: try and see what's happening) colors: must be 4 colors for the first patch, 2 colors for the following patches</li></ul> 12486 * @param array $coords_min minimum value used by the coordinates. If a coordinate's value is smaller than this it will be cut to coords_min. default: 0 12487 * @param array $coords_max maximum value used by the coordinates. If a coordinate's value is greater than this it will be cut to coords_max. default: 1 12488 * @param boolean $antialias A flag indicating whether to filter the shading function to prevent aliasing artifacts. 12489 * @author Andreas Würmser, Nicola Asuni 12490 * @since 3.1.000 (2008-06-09) 12491 * @access public 12492 */ 12493 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) { 12494 $this->Clip($x, $y, $w, $h); 12495 $n = count($this->gradients) + 1; 12496 $this->gradients[$n] = array(); 12497 $this->gradients[$n]['type'] = 6; //coons patch mesh 12498 $this->gradients[$n]['coords'] = array(); 12499 $this->gradients[$n]['antialias'] = $antialias; 12500 $this->gradients[$n]['colors'] = array(); 12501 $this->gradients[$n]['transparency'] = false; 12502 //check the coords array if it is the simple array or the multi patch array 12503 if (!isset($coords[0]['f'])) { 12504 //simple array -> convert to multi patch array 12505 if (!isset($col1[1])) { 12506 $col1[1] = $col1[2] = $col1[0]; 12507 } 12508 if (!isset($col2[1])) { 12509 $col2[1] = $col2[2] = $col2[0]; 12510 } 12511 if (!isset($col3[1])) { 12512 $col3[1] = $col3[2] = $col3[0]; 12513 } 12514 if (!isset($col4[1])) { 12515 $col4[1] = $col4[2] = $col4[0]; 12516 } 12517 $patch_array[0]['f'] = 0; 12518 $patch_array[0]['points'] = $coords; 12519 $patch_array[0]['colors'][0]['r'] = $col1[0]; 12520 $patch_array[0]['colors'][0]['g'] = $col1[1]; 12521 $patch_array[0]['colors'][0]['b'] = $col1[2]; 12522 $patch_array[0]['colors'][1]['r'] = $col2[0]; 12523 $patch_array[0]['colors'][1]['g'] = $col2[1]; 12524 $patch_array[0]['colors'][1]['b'] = $col2[2]; 12525 $patch_array[0]['colors'][2]['r'] = $col3[0]; 12526 $patch_array[0]['colors'][2]['g'] = $col3[1]; 12527 $patch_array[0]['colors'][2]['b'] = $col3[2]; 12528 $patch_array[0]['colors'][3]['r'] = $col4[0]; 12529 $patch_array[0]['colors'][3]['g'] = $col4[1]; 12530 $patch_array[0]['colors'][3]['b'] = $col4[2]; 12531 } else { 12532 //multi patch array 12533 $patch_array = $coords; 12534 } 12535 $bpcd = 65535; //16 bits per coordinate 12536 //build the data stream 12537 $this->gradients[$n]['stream'] = ''; 12538 $count_patch = count($patch_array); 12539 for ($i=0; $i < $count_patch; ++$i) { 12540 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit 12541 $count_points = count($patch_array[$i]['points']); 12542 for ($j=0; $j < $count_points; ++$j) { 12543 //each point as 16 bit 12544 $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j] - $coords_min) / ($coords_max - $coords_min)) * $bpcd; 12545 if ($patch_array[$i]['points'][$j] < 0) { 12546 $patch_array[$i]['points'][$j] = 0; 12547 } 12548 if ($patch_array[$i]['points'][$j] > $bpcd) { 12549 $patch_array[$i]['points'][$j] = $bpcd; 12550 } 12551 $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] / 256)); 12552 $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] % 256)); 12553 } 12554 $count_cols = count($patch_array[$i]['colors']); 12555 for ($j=0; $j < $count_cols; ++$j) { 12556 //each color component as 8 bit 12557 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']); 12558 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']); 12559 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']); 12560 } 12561 } 12562 //paint the gradient 12563 $this->_out('/Sh'.$n.' sh'); 12564 //restore previous Graphic State 12565 $this->_out('Q'); 12566 } 12567 12568 /** 12569 * Set a rectangular clipping area. 12570 * @param float $x abscissa of the top left corner of the rectangle (or top right corner for RTL mode). 12571 * @param float $y ordinate of the top left corner of the rectangle. 12572 * @param float $w width of the rectangle. 12573 * @param float $h height of the rectangle. 12574 * @author Andreas Würmser, Nicola Asuni 12575 * @since 3.1.000 (2008-06-09) 12576 * @access protected 12577 */ 12578 protected function Clip($x, $y, $w, $h) { 12579 if ($this->rtl) { 12580 $x = $this->w - $x - $w; 12581 } 12582 //save current Graphic State 12583 $s = 'q'; 12584 //set clipping area 12585 $s .= sprintf(' %.2F %.2F %.2F %.2F re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k); 12586 //set up transformation matrix for gradient 12587 $s .= sprintf(' %.3F 0 0 %.3F %.3F %.3F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k); 12588 $this->_out($s); 12589 } 12590 12591 /** 12592 * Output gradient. 12593 * @param int $type type of gradient (1 Function-based shading; 2 Axial shading; 3 Radial shading; 4 Free-form Gouraud-shaded triangle mesh; 5 Lattice-form Gouraud-shaded triangle mesh; 6 Coons patch mesh; 7 Tensor-product patch mesh). (Not all types are currently supported) 12594 * @param array $coords array of coordinates. 12595 * @param array $stops array gradient color components: color = array of GRAY, RGB or CMYK color components; offset = (0 to 1) represents a location along the gradient vector; exponent = exponent of the exponential interpolation function (default = 1). 12596 * @param array $background An array of colour components appropriate to the colour space, specifying a single background colour value. 12597 * @param boolean $antialias A flag indicating whether to filter the shading function to prevent aliasing artifacts. 12598 * @author Nicola Asuni 12599 * @since 3.1.000 (2008-06-09) 12600 * @access public 12601 */ 12602 public function Gradient($type, $coords, $stops, $background=array(), $antialias=false) { 12603 $n = count($this->gradients) + 1; 12604 $this->gradients[$n] = array(); 12605 $this->gradients[$n]['type'] = $type; 12606 $this->gradients[$n]['coords'] = $coords; 12607 $this->gradients[$n]['antialias'] = $antialias; 12608 $this->gradients[$n]['colors'] = array(); 12609 $this->gradients[$n]['transparency'] = false; 12610 // color space 12611 $numcolspace = count($stops[0]['color']); 12612 $bcolor = array_values($background); 12613 switch($numcolspace) { 12614 case 4: { // CMYK 12615 $this->gradients[$n]['colspace'] = 'DeviceCMYK'; 12616 if (!empty($background)) { 12617 $this->gradients[$n]['background'] = sprintf('%.3F %.3F %.3F %.3F', $bcolor[0]/100, $bcolor[1]/100, $bcolor[2]/100, $bcolor[3]/100); 12618 } 12619 break; 12620 } 12621 case 3: { // RGB 12622 $this->gradients[$n]['colspace'] = 'DeviceRGB'; 12623 if (!empty($background)) { 12624 $this->gradients[$n]['background'] = sprintf('%.3F %.3F %.3F', $bcolor[0]/255, $bcolor[1]/255, $bcolor[2]/255); 12625 } 12626 break; 12627 } 12628 case 1: { // Gray scale 12629 $this->gradients[$n]['colspace'] = 'DeviceGray'; 12630 if (!empty($background)) { 12631 $this->gradients[$n]['background'] = sprintf('%.3F', $bcolor[0]/255); 12632 } 12633 break; 12634 } 12635 } 12636 $num_stops = count($stops); 12637 $last_stop_id = $num_stops - 1; 12638 foreach ($stops as $key => $stop) { 12639 $this->gradients[$n]['colors'][$key] = array(); 12640 // offset represents a location along the gradient vector 12641 if (isset($stop['offset'])) { 12642 $this->gradients[$n]['colors'][$key]['offset'] = $stop['offset']; 12643 } else { 12644 if ($key == 0) { 12645 $this->gradients[$n]['colors'][$key]['offset'] = 0; 12646 } elseif ($key == $last_stop_id) { 12647 $this->gradients[$n]['colors'][$key]['offset'] = 1; 12648 } else { 12649 $offsetstep = (1 - $this->gradients[$n]['colors'][($key - 1)]['offset']) / ($num_stops - $key); 12650 $this->gradients[$n]['colors'][$key]['offset'] = $this->gradients[$n]['colors'][($key - 1)]['offset'] + $offsetstep; 12651 } 12652 } 12653 if (isset($stop['opacity'])) { 12654 $this->gradients[$n]['colors'][$key]['opacity'] = $stop['opacity']; 12655 if ($stop['opacity'] < 1) { 12656 $this->gradients[$n]['transparency'] = true; 12657 } 12658 } else { 12659 $this->gradients[$n]['colors'][$key]['opacity'] = 1; 12660 } 12661 // exponent for the exponential interpolation function 12662 if (isset($stop['exponent'])) { 12663 $this->gradients[$n]['colors'][$key]['exponent'] = $stop['exponent']; 12664 } else { 12665 $this->gradients[$n]['colors'][$key]['exponent'] = 1; 12666 } 12667 // set colors 12668 $color = array_values($stop['color']); 12669 switch($numcolspace) { 12670 case 4: { // CMYK 12671 $this->gradients[$n]['colors'][$key]['color'] = sprintf('%.3F %.3F %.3F %.3F', $color[0]/100, $color[1]/100, $color[2]/100, $color[3]/100); 12672 break; 12673 } 12674 case 3: { // RGB 12675 $this->gradients[$n]['colors'][$key]['color'] = sprintf('%.3F %.3F %.3F', $color[0]/255, $color[1]/255, $color[2]/255); 12676 break; 12677 } 12678 case 1: { // Gray scale 12679 $this->gradients[$n]['colors'][$key]['color'] = sprintf('%.3F', $color[0]/255); 12680 break; 12681 } 12682 } 12683 } 12684 if ($this->gradients[$n]['transparency']) { 12685 // paint luminosity gradient 12686 $this->_out('/TGS'.$n.' gs'); 12687 } 12688 //paint the gradient 12689 $this->_out('/Sh'.$n.' sh'); 12690 //restore previous Graphic State 12691 $this->_out('Q'); 12692 } 12693 12694 /** 12695 * Output gradient shaders. 12696 * @author Nicola Asuni 12697 * @since 3.1.000 (2008-06-09) 12698 * @access protected 12699 */ 12700 function _putshaders() { 12701 $idt = count($this->gradients); //index for transparency gradients 12702 foreach ($this->gradients as $id => $grad) { 12703 if (($grad['type'] == 2) OR ($grad['type'] == 3)) { 12704 $this->_newobj(); 12705 $fc = $this->n; 12706 $out = '<<'; 12707 $out .= ' /FunctionType 3'; 12708 $out .= ' /Domain [0 1]'; 12709 $functions = ''; 12710 $bounds = ''; 12711 $encode = ''; 12712 $i = 1; 12713 $num_cols = count($grad['colors']); 12714 $lastcols = $num_cols - 1; 12715 for ($i = 1; $i < $num_cols; ++$i) { 12716 $functions .= ($fc + $i).' 0 R '; 12717 if ($i < $lastcols) { 12718 $bounds .= sprintf('%.3F ', $grad['colors'][$i]['offset']); 12719 } 12720 $encode .= '0 1 '; 12721 } 12722 $out .= ' /Functions ['.trim($functions).']'; 12723 $out .= ' /Bounds ['.trim($bounds).']'; 12724 $out .= ' /Encode ['.trim($encode).']'; 12725 $out .= ' >>'; 12726 $out .= ' endobj'; 12727 $this->_out($out); 12728 for ($i = 1; $i < $num_cols; ++$i) { 12729 $this->_newobj(); 12730 $out = '<<'; 12731 $out .= ' /FunctionType 2'; 12732 $out .= ' /Domain [0 1]'; 12733 $out .= ' /C0 ['.$grad['colors'][($i - 1)]['color'].']'; 12734 $out .= ' /C1 ['.$grad['colors'][$i]['color'].']'; 12735 $out .= ' /N '.$grad['colors'][$i]['exponent']; 12736 $out .= ' >>'; 12737 $out .= ' endobj'; 12738 $this->_out($out); 12739 } 12740 // set transparency fuctions 12741 if ($grad['transparency']) { 12742 $this->_newobj(); 12743 $ft = $this->n; 12744 $out = '<<'; 12745 $out .= ' /FunctionType 3'; 12746 $out .= ' /Domain [0 1]'; 12747 $functions = ''; 12748 $i = 1; 12749 $num_cols = count($grad['colors']); 12750 for ($i = 1; $i < $num_cols; ++$i) { 12751 $functions .= ($ft + $i).' 0 R '; 12752 } 12753 $out .= ' /Functions ['.trim($functions).']'; 12754 $out .= ' /Bounds ['.trim($bounds).']'; 12755 $out .= ' /Encode ['.trim($encode).']'; 12756 $out .= ' >>'; 12757 $out .= ' endobj'; 12758 $this->_out($out); 12759 for ($i = 1; $i < $num_cols; ++$i) { 12760 $this->_newobj(); 12761 $out = '<<'; 12762 $out .= ' /FunctionType 2'; 12763 $out .= ' /Domain [0 1]'; 12764 $out .= ' /C0 ['.$grad['colors'][($i - 1)]['opacity'].']'; 12765 $out .= ' /C1 ['.$grad['colors'][$i]['opacity'].']'; 12766 $out .= ' /N '.$grad['colors'][$i]['exponent']; 12767 $out .= ' >>'; 12768 $out .= ' endobj'; 12769 $this->_out($out); 12770 } 12771 } 12772 } 12773 // set shading object 12774 $this->_newobj(); 12775 $out = '<< /ShadingType '.$grad['type']; 12776 if (isset($grad['colspace'])) { 12777 $out .= ' /ColorSpace /'.$grad['colspace']; 12778 } else { 12779 $out .= ' /ColorSpace /DeviceRGB'; 12780 } 12781 if (isset($grad['background']) AND !empty($grad['background'])) { 12782 $out .= ' /Background ['.$grad['background'].']'; 12783 } 12784 if (isset($grad['antialias']) AND ($grad['antialias'] === true)) { 12785 $out .= ' /AntiAlias true'; 12786 } 12787 if ($grad['type'] == 2) { 12788 $out .= ' '.sprintf('/Coords [%.3F %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]); 12789 $out .= ' /Domain [0 1]'; 12790 $out .= ' /Function '.$fc.' 0 R'; 12791 $out .= ' /Extend [true true]'; 12792 $out .= ' >>'; 12793 } elseif ($grad['type'] == 3) { 12794 //x0, y0, r0, x1, y1, r1 12795 //at this this time radius of inner circle is 0 12796 $out .= ' '.sprintf('/Coords [%.3F %.3F 0 %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]); 12797 $out .= ' /Domain [0 1]'; 12798 $out .= ' /Function '.$fc.' 0 R'; 12799 $out .= ' /Extend [true true]'; 12800 $out .= ' >>'; 12801 } elseif ($grad['type'] == 6) { 12802 $out .= ' /BitsPerCoordinate 16'; 12803 $out .= ' /BitsPerComponent 8'; 12804 $out .= ' /Decode[0 1 0 1 0 1 0 1 0 1]'; 12805 $out .= ' /BitsPerFlag 8'; 12806 $out .= ' /Length '.strlen($grad['stream']); 12807 $out .= ' >>'; 12808 $out .= ' '.$this->_getstream($grad['stream']); 12809 } 12810 $out .= ' endobj'; 12811 $this->_out($out); 12812 if ($grad['transparency']) { 12813 $shading_transparency = preg_replace('/\/ColorSpace \/[^\s]+/si', '/ColorSpace /DeviceGray', $out); 12814 $shading_transparency = preg_replace('/\/Function [0-9]+ /si', '/Function '.$ft.' ', $shading_transparency); 12815 } 12816 $this->gradients[$id]['id'] = $this->n; 12817 // set pattern object 12818 $this->_newobj(); 12819 $out = '<< /Type /Pattern /PatternType 2'; 12820 $out .= ' /Shading '.$this->gradients[$id]['id'].' 0 R'; 12821 $out .= ' >> endobj'; 12822 $this->_out($out); 12823 $this->gradients[$id]['pattern'] = $this->n; 12824 // set shading and pattern for transparency mask 12825 if ($grad['transparency']) { 12826 // luminosity pattern 12827 $idgs = $id + $idt; 12828 $this->_newobj(); 12829 $this->_out($shading_transparency); 12830 $this->gradients[$idgs]['id'] = $this->n; 12831 $this->_newobj(); 12832 $out = '<< /Type /Pattern /PatternType 2'; 12833 $out .= ' /Shading '.$this->gradients[$idgs]['id'].' 0 R'; 12834 $out .= ' >> endobj'; 12835 $this->_out($out); 12836 $this->gradients[$idgs]['pattern'] = $this->n; 12837 // luminosity XObject 12838 $this->_newobj(); 12839 $filter = ($this->compress)?' /Filter /FlateDecode':''; 12840 $out = '<< /Type /XObject /Subtype /Form /FormType 1'.$filter; 12841 $stream = 'q /a0 gs /Pattern cs /p'.$idgs.' scn 0 0 '.$this->wPt.' '.$this->hPt.' re f Q'; 12842 $out .= ' /Length '.strlen($stream); 12843 $out .= ' /BBox [0 0 '.$this->wPt.' '.$this->hPt.']'; 12844 $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceGray >>'; 12845 $out .= ' /Resources <<'; 12846 $out .= ' /ExtGState << /a0 << /ca 1 /CA 1 >> >>'; 12847 $out .= ' /Pattern << /p'.$idgs.' '.$this->gradients[$idgs]['pattern'].' 0 R >>'; 12848 $out .= ' >>'; 12849 $out .= ' >> '; 12850 $out .= $this->_getstream($stream); 12851 $out .= ' endobj'; 12852 $this->_out($out); 12853 // SMask 12854 $this->_newobj(); 12855 $out = '<< /Type /Mask /S /Luminosity /G '.($this->n - 1).' 0 R >> endobj'; 12856 $this->_out($out); 12857 // ExtGState 12858 $this->_newobj(); 12859 $out = '<< /Type /ExtGState /SMask '.($this->n - 1).' 0 R /AIS false >> endobj'; 12860 $this->_out($out); 12861 $this->extgstates[] = array('n' => $this->n, 'name' => 'TGS'.$id); 12862 } 12863 } 12864 } 12865 12866 /** 12867 * Draw the sector of a circle. 12868 * It can be used for instance to render pie charts. 12869 * @param float $xc abscissa of the center. 12870 * @param float $yc ordinate of the center. 12871 * @param float $r radius. 12872 * @param float $a start angle (in degrees). 12873 * @param float $b end angle (in degrees). 12874 * @param string $style Style of rendering. See the getPathPaintOperator() function for more information. 12875 * @param float $cw: indicates whether to go clockwise (default: true). 12876 * @param float $o: origin of angles (0 for 3 o'clock, 90 for noon, 180 for 9 o'clock, 270 for 6 o'clock). Default: 90. 12877 * @author Maxime Delorme, Nicola Asuni 12878 * @since 3.1.000 (2008-06-09) 12879 * @access public 12880 */ 12881 public function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) { 12882 $this->PieSectorXY($xc, $yc, $r, $r, $a, $b, $style, $cw, $o); 12883 } 12884 12885 /** 12886 * Draw the sector of an ellipse. 12887 * It can be used for instance to render pie charts. 12888 * @param float $xc abscissa of the center. 12889 * @param float $yc ordinate of the center. 12890 * @param float $rx the x-axis radius. 12891 * @param float $ry the y-axis radius. 12892 * @param float $a start angle (in degrees). 12893 * @param float $b end angle (in degrees). 12894 * @param string $style Style of rendering. See the getPathPaintOperator() function for more information. 12895 * @param float $cw: indicates whether to go clockwise. 12896 * @param float $o: origin of angles (0 for 3 o'clock, 90 for noon, 180 for 9 o'clock, 270 for 6 o'clock). 12897 * @param integer $nc Number of curves used to draw a 90 degrees portion of arc. 12898 * @author Maxime Delorme, Nicola Asuni 12899 * @since 3.1.000 (2008-06-09) 12900 * @access public 12901 */ 12902 public function PieSectorXY($xc, $yc, $rx, $ry, $a, $b, $style='FD', $cw=false, $o=0, $nc=2) { 12903 if ($this->rtl) { 12904 $xc = $this->w - $xc; 12905 } 12906 $op = $this->getPathPaintOperator($style); 12907 if ($op == 'f') { 12908 $line_style = array(); 12909 } 12910 if ($cw) { 12911 $d = $b; 12912 $b = 360 - $a + $o; 12913 $a = 360 - $d + $o; 12914 } else { 12915 $b += $o; 12916 $a += $o; 12917 } 12918 $this->_outellipticalarc($xc, $yc, $rx, $ry, 0, $a, $b, true, $nc); 12919 $this->_out($op); 12920 } 12921 12922 /** 12923 * Embed vector-based Adobe Illustrator (AI) or AI-compatible EPS files. 12924 * NOTE: EPS is not yet fully implemented, use the setRasterizeVectorImages() method to enable/disable rasterization of SVG images using ImageMagick library. 12925 * Only vector drawing is supported, not text or bitmap. 12926 * Although the script was successfully tested with various AI format versions, best results are probably achieved with files that were exported in the AI3 format (tested with Illustrator CS2, Freehand MX and Photoshop CS2). 12927 * @param string $file Name of the file containing the image. 12928 * @param float $x Abscissa of the upper-left corner. 12929 * @param float $y Ordinate of the upper-left corner. 12930 * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated. 12931 * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated. 12932 * @param mixed $link URL or identifier returned by AddLink(). 12933 * @param boolean useBoundingBox specifies whether to position the bounding box (true) or the complete canvas (false) at location (x,y). Default value is true. 12934 * @param string $align Indicates the alignment of the pointer next to image insertion relative to image height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul> 12935 * @param string $palign Allows to center or align the image on the current line. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul> 12936 * @param mixed $border Indicates if borders must be drawn around the image. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> 12937 * @param boolean $fitonpage if true the image is resized to not exceed page dimensions. 12938 * @author Valentin Schmidt, Nicola Asuni 12939 * @since 3.1.000 (2008-06-09) 12940 * @access public 12941 */ 12942 public function ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0, $fitonpage=false) { 12943 if ($this->rasterize_vector_images) { 12944 // convert SVG to raster image using GD or ImageMagick libraries 12945 return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage); 12946 } 12947 if ($x === '') { 12948 $x = $this->x; 12949 } 12950 if ($y === '') { 12951 $y = $this->y; 12952 } 12953 $k = $this->k; 12954 $data = file_get_contents($file); 12955 if ($data === false) { 12956 $this->Error('EPS file not found: '.$file); 12957 } 12958 $regs = array(); 12959 // EPS/AI compatibility check (only checks files created by Adobe Illustrator!) 12960 preg_match("/%%Creator:([^\r\n]+)/", $data, $regs); # find Creator 12961 if (count($regs) > 1) { 12962 $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0" 12963 if (strpos($version_str, 'Adobe Illustrator') !== false) { 12964 $versexp = explode(' ', $version_str); 12965 $version = (float)array_pop($versexp); 12966 if ($version >= 9) { 12967 $this->Error('This version of Adobe Illustrator file is not supported: '.$file); 12968 } 12969 } 12970 } 12971 // strip binary bytes in front of PS-header 12972 $start = strpos($data, '%!PS-Adobe'); 12973 if ($start > 0) { 12974 $data = substr($data, $start); 12975 } 12976 // find BoundingBox params 12977 preg_match("/%%BoundingBox:([^\r\n]+)/", $data, $regs); 12978 if (count($regs) > 1) { 12979 list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1])); 12980 } else { 12981 $this->Error('No BoundingBox found in EPS file: '.$file); 12982 } 12983 $start = strpos($data, '%%EndSetup'); 12984 if ($start === false) { 12985 $start = strpos($data, '%%EndProlog'); 12986 } 12987 if ($start === false) { 12988 $start = strpos($data, '%%BoundingBox'); 12989 } 12990 $data = substr($data, $start); 12991 $end = strpos($data, '%%PageTrailer'); 12992 if ($end===false) { 12993 $end = strpos($data, 'showpage'); 12994 } 12995 if ($end) { 12996 $data = substr($data, 0, $end); 12997 } 12998 // calculate image width and height on document 12999 if (($w <= 0) AND ($h <= 0)) { 13000 $w = ($x2 - $x1) / $k; 13001 $h = ($y2 - $y1) / $k; 13002 } elseif ($w <= 0) { 13003 $w = ($x2-$x1) / $k * ($h / (($y2 - $y1) / $k)); 13004 } elseif ($h <= 0) { 13005 $h = ($y2 - $y1) / $k * ($w / (($x2 - $x1) / $k)); 13006 } 13007 // Check whether we need a new page first as this does not fit 13008 $prev_x = $this->x; 13009 if ($this->checkPageBreak($h, $y)) { 13010 $y = $this->y; 13011 if ($this->rtl) { 13012 $x += ($prev_x - $this->x); 13013 } else { 13014 $x += ($this->x - $prev_x); 13015 } 13016 } 13017 // resize image to be contained on a single page 13018 if ($fitonpage) { 13019 $ratio_wh = $w / $h; 13020 if (($y + $h) > $this->PageBreakTrigger) { 13021 $h = $this->PageBreakTrigger - $y; 13022 $w = $h * $ratio_wh; 13023 } 13024 if (($x + $w) > ($this->w - $this->rMargin)) { 13025 $w = $this->w - $this->rMargin - $x; 13026 $h = $w / $ratio_wh; 13027 } 13028 } 13029 // set scaling factors 13030 $scale_x = $w / (($x2 - $x1) / $k); 13031 $scale_y = $h / (($y2 - $y1) / $k); 13032 // set alignment 13033 $this->img_rb_y = $y + $h; 13034 // set alignment 13035 if ($this->rtl) { 13036 if ($palign == 'L') { 13037 $ximg = $this->lMargin; 13038 } elseif ($palign == 'C') { 13039 $ximg = ($this->w - $w) / 2; 13040 } elseif ($palign == 'R') { 13041 $ximg = $this->w - $this->rMargin - $w; 13042 } else { 13043 $ximg = $this->w - $x - $w; 13044 } 13045 $this->img_rb_x = $ximg; 13046 } else { 13047 if ($palign == 'L') { 13048 $ximg = $this->lMargin; 13049 } elseif ($palign == 'C') { 13050 $ximg = ($this->w - $w) / 2; 13051 } elseif ($palign == 'R') { 13052 $ximg = $this->w - $this->rMargin - $w; 13053 } else { 13054 $ximg = $x; 13055 } 13056 $this->img_rb_x = $ximg + $w; 13057 } 13058 if ($useBoundingBox) { 13059 $dx = $ximg * $k - $x1; 13060 $dy = $y * $k - $y1; 13061 } else { 13062 $dx = $ximg * $k; 13063 $dy = $y * $k; 13064 } 13065 // save the current graphic state 13066 $this->_out('q'.$this->epsmarker); 13067 // translate 13068 $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', 1, 0, 0, 1, $dx, $dy + ($this->hPt - (2 * $y * $k) - ($y2 - $y1)))); 13069 // scale 13070 if (isset($scale_x)) { 13071 $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $scale_x, 0, 0, $scale_y, $x1 * (1 - $scale_x), $y2 * (1 - $scale_y))); 13072 } 13073 // handle pc/unix/mac line endings 13074 preg_match('/[\r\n]+/s', $data, $regs); 13075 $lines = explode($regs[0], $data); 13076 $u=0; 13077 $cnt = count($lines); 13078 for ($i=0; $i < $cnt; ++$i) { 13079 $line = $lines[$i]; 13080 if (($line == '') OR ($line{0} == '%')) { 13081 continue; 13082 } 13083 $len = strlen($line); 13084 $chunks = explode(' ', $line); 13085 $cmd = array_pop($chunks); 13086 // RGB 13087 if (($cmd == 'Xa') OR ($cmd == 'XA')) { 13088 $b = array_pop($chunks); 13089 $g = array_pop($chunks); 13090 $r = array_pop($chunks); 13091 $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! 13092 continue; 13093 } 13094 switch ($cmd) { 13095 case 'm': 13096 case 'l': 13097 case 'v': 13098 case 'y': 13099 case 'c': 13100 case 'k': 13101 case 'K': 13102 case 'g': 13103 case 'G': 13104 case 's': 13105 case 'S': 13106 case 'J': 13107 case 'j': 13108 case 'w': 13109 case 'M': 13110 case 'd': 13111 case 'n': { 13112 $this->_out($line); 13113 break; 13114 } 13115 case 'x': {// custom fill color 13116 list($c,$m,$y,$k) = $chunks; 13117 $this->_out(''.$c.' '.$m.' '.$y.' '.$k.' k'); 13118 break; 13119 } 13120 case 'X': { // custom stroke color 13121 list($c,$m,$y,$k) = $chunks; 13122 $this->_out(''.$c.' '.$m.' '.$y.' '.$k.' K'); 13123 break; 13124 } 13125 case 'Y': 13126 case 'N': 13127 case 'V': 13128 case 'L': 13129 case 'C': { 13130 $line{$len-1} = strtolower($cmd); 13131 $this->_out($line); 13132 break; 13133 } 13134 case 'b': 13135 case 'B': { 13136 $this->_out($cmd . '*'); 13137 break; 13138 } 13139 case 'f': 13140 case 'F': { 13141 if ($u > 0) { 13142 $isU = false; 13143 $max = min($i+5, $cnt); 13144 for ($j=$i+1; $j < $max; ++$j) { 13145 $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U'))); 13146 } 13147 if ($isU) { 13148 $this->_out('f*'); 13149 } 13150 } else { 13151 $this->_out('f*'); 13152 } 13153 break; 13154 } 13155 case '*u': { 13156 ++$u; 13157 break; 13158 } 13159 case '*U': { 13160 --$u; 13161 break; 13162 } 13163 } 13164 } 13165 // restore previous graphic state 13166 $this->_out($this->epsmarker.'Q'); 13167 if (!empty($border)) { 13168 $bx = $x; 13169 $by = $y; 13170 $this->x = $ximg; 13171 if ($this->rtl) { 13172 $this->x += $w; 13173 } 13174 $this->y = $y; 13175 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0); 13176 $this->x = $bx; 13177 $this->y = $by; 13178 } 13179 if ($link) { 13180 $this->Link($ximg, $y, $w, $h, $link, 0); 13181 } 13182 // set pointer to align the successive text/objects 13183 switch($align) { 13184 case 'T':{ 13185 $this->y = $y; 13186 $this->x = $this->img_rb_x; 13187 break; 13188 } 13189 case 'M':{ 13190 $this->y = $y + round($h/2); 13191 $this->x = $this->img_rb_x; 13192 break; 13193 } 13194 case 'B':{ 13195 $this->y = $this->img_rb_y; 13196 $this->x = $this->img_rb_x; 13197 break; 13198 } 13199 case 'N':{ 13200 $this->SetY($this->img_rb_y); 13201 break; 13202 } 13203 default:{ 13204 break; 13205 } 13206 } 13207 $this->endlinex = $this->img_rb_x; 13208 } 13209 13210 /** 13211 * Set document barcode. 13212 * @param string $bc barcode 13213 * @access public 13214 */ 13215 public function setBarcode($bc='') { 13216 $this->barcode = $bc; 13217 } 13218 13219 /** 13220 * Get current barcode. 13221 * @return string 13222 * @access public 13223 * @since 4.0.012 (2008-07-24) 13224 */ 13225 public function getBarcode() { 13226 return $this->barcode; 13227 } 13228 13229 /** 13230 * Print a Linear Barcode. 13231 * @param string $code code to print 13232 * @param string $type type of barcode (see barcodes.php for supported formats). 13233 * @param int $x x position in user units 13234 * @param int $y y position in user units 13235 * @param int $w width in user units 13236 * @param int $h height in user units 13237 * @param float $xres width of the smallest bar in user units 13238 * @param array $style array of options:<ul><li>string $style['position'] barcode position inside the specified width: L = left (default for LTR); C = center; R = right (default for RTL); S = stretch</li><li>boolean $style['border'] if true prints a border around the barcode</li><li>int $style['padding'] padding to leave around the barcode in user units (set to 'auto' for automatic padding)</li><li>array $style['fgcolor'] color array for bars and text</li><li>mixed $style['bgcolor'] color array for background or false for transparent</li><li>boolean $style["text"] boolean if true prints text below the barcode</li><li>string $style['font'] font name for text</li><li>int $style['fontsize'] font size for text</li><li>int $style['stretchtext']: 0 = disabled; 1 = horizontal scaling only if necessary; 2 = forced horizontal scaling; 3 = character spacing only if necessary; 4 = forced character spacing</li></ul> 13239 * @param string $align Indicates the alignment of the pointer next to barcode insertion relative to barcode height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul> 13240 * @author Nicola Asuni 13241 * @since 3.1.000 (2008-06-09) 13242 * @access public 13243 */ 13244 public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres=0.4, $style='', $align='') { 13245 if ($this->empty_string($code)) { 13246 return; 13247 } 13248 require_once(dirname(__FILE__).'/barcodes.php'); 13249 // save current graphic settings 13250 $gvars = $this->getGraphicVars(); 13251 // create new barcode object 13252 $barcodeobj = new TCPDFBarcode($code, $type); 13253 $arrcode = $barcodeobj->getBarcodeArray(); 13254 if ($arrcode === false) { 13255 $this->Error('Error in 1D barcode string'); 13256 } 13257 // set default values 13258 if (!isset($style['position'])) { 13259 if ($this->rtl) { 13260 $style['position'] = 'R'; 13261 } else { 13262 $style['position'] = 'L'; 13263 } 13264 } 13265 if (!isset($style['fgcolor'])) { 13266 $style['fgcolor'] = array(0,0,0); // default black 13267 } 13268 if (!isset($style['bgcolor'])) { 13269 $style['bgcolor'] = false; // default transparent 13270 } 13271 if (!isset($style['border'])) { 13272 $style['border'] = false; 13273 } 13274 $fontsize = 0; 13275 if (!isset($style['text'])) { 13276 $style['text'] = false; 13277 } 13278 if ($style['text'] AND isset($style['font'])) { 13279 if (isset($style['fontsize'])) { 13280 $fontsize = $style['fontsize']; 13281 } 13282 $this->SetFont($style['font'], '', $fontsize); 13283 } 13284 if (!isset($style['stretchtext'])) { 13285 $style['stretchtext'] = 4; 13286 } 13287 // set foreground color 13288 $this->SetDrawColorArray($style['fgcolor']); 13289 $this->SetTextColorArray($style['fgcolor']); 13290 if ($this->empty_string($w) OR ($w <= 0)) { 13291 if ($this->rtl) { 13292 $w = $this->x - $this->lMargin; 13293 } else { 13294 $w = $this->w - $this->rMargin - $this->x; 13295 } 13296 } 13297 if ($this->empty_string($x)) { 13298 $x = $this->GetX(); 13299 } 13300 if ($this->rtl) { 13301 $x = $this->w - $x; 13302 } 13303 if ($this->empty_string($y)) { 13304 $y = $this->GetY(); 13305 } 13306 if ($this->empty_string($xres)) { 13307 $xres = 0.4; 13308 } 13309 if ($this->empty_string($h) OR ($h <= 0)) { 13310 $h = $w / 3; 13311 } 13312 // padding 13313 if (!isset($style['padding'])) { 13314 $style['padding'] = 0; 13315 } elseif ($style['padding'] === 'auto') { 13316 $style['padding'] = $h / 4; 13317 } 13318 $fbw = ($arrcode['maxw'] * $xres) + (2 * $style['padding']); 13319 $extraspace = ($this->cell_height_ratio * $fontsize / $this->k) + (2 * $style['padding']); 13320 $prev_x = $this->x; 13321 // maximum bar height 13322 $barh = $h; 13323 $h += $extraspace; 13324 if ($this->checkPageBreak($h, $y)) { 13325 $y = $this->GetY() + $this->cMargin; 13326 if ($this->rtl) { 13327 $x += ($prev_x - $this->x); 13328 } else { 13329 $x += ($this->x - $prev_x); 13330 } 13331 } 13332 switch ($style['position']) { 13333 case 'L': { // left 13334 if ($this->rtl) { 13335 $xpos = $x - $w; 13336 } else { 13337 $xpos = $x; 13338 } 13339 break; 13340 } 13341 case 'C': { // center 13342 $xdiff = (($w - $fbw) / 2); 13343 if ($this->rtl) { 13344 $xpos = $x - $w + $xdiff; 13345 } else { 13346 $xpos = $x + $xdiff; 13347 } 13348 break; 13349 } 13350 case 'R': { // right 13351 if ($this->rtl) { 13352 $xpos = $x - $fbw; 13353 } else { 13354 $xpos = $x + $w - $fbw; 13355 } 13356 break; 13357 } 13358 case 'S': { // stretch 13359 $fbw = $w; 13360 $xres = ($w - (2 * $style['padding'])) / $arrcode['maxw']; 13361 if ($this->rtl) { 13362 $xpos = $x - $w; 13363 } else { 13364 $xpos = $x; 13365 } 13366 break; 13367 } 13368 } 13369 $xpos_rect = $xpos; 13370 $xpos = $xpos_rect + $style['padding']; 13371 $xpos_text = $xpos; 13372 // barcode is always printed in LTR direction 13373 $tempRTL = $this->rtl; 13374 $this->rtl = false; 13375 // print background color 13376 if ($style['bgcolor']) { 13377 $this->Rect($xpos_rect, $y, $fbw, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']); 13378 } elseif ($style['border']) { 13379 $this->Rect($xpos_rect, $y, $fbw, $h, 'D'); 13380 } 13381 // print bars 13382 if ($arrcode !== false) { 13383 foreach ($arrcode['bcode'] as $k => $v) { 13384 $bw = ($v['w'] * $xres); 13385 if ($v['t']) { 13386 // draw a vertical bar 13387 $ypos = $y + $style['padding'] + ($v['p'] * $barh / $arrcode['maxh']); 13388 $this->Rect($xpos, $ypos, $bw, ($v['h'] * $barh / $arrcode['maxh']), 'F', array(), $style['fgcolor']); 13389 } 13390 $xpos += $bw; 13391 } 13392 } 13393 // print text 13394 if ($style['text']) { 13395 // print text 13396 $this->x = $xpos_text; 13397 $this->y = $y + $style['padding'] + $barh; 13398 $this->Cell(($arrcode['maxw'] * $xres), ($this->cell_height_ratio * $fontsize / $this->k), $code, 0, 0, 'C', 0, '', $style['stretchtext']); 13399 } 13400 // restore original direction 13401 $this->rtl = $tempRTL; 13402 // restore previous settings 13403 $this->setGraphicVars($gvars); 13404 // set bottomcoordinates 13405 $this->img_rb_y = $y + $h; 13406 if ($this->rtl) { 13407 // set left side coordinate 13408 $this->img_rb_x = ($this->w - $x - $w); 13409 } else { 13410 // set right side coordinate 13411 $this->img_rb_x = $x + $w; 13412 } 13413 // set pointer to align the successive text/objects 13414 switch($align) { 13415 case 'T':{ 13416 $this->y = $y; 13417 $this->x = $this->img_rb_x; 13418 break; 13419 } 13420 case 'M':{ 13421 $this->y = $y + round($h/2); 13422 $this->x = $this->img_rb_x; 13423 break; 13424 } 13425 case 'B':{ 13426 $this->y = $this->img_rb_y; 13427 $this->x = $this->img_rb_x; 13428 break; 13429 } 13430 case 'N':{ 13431 $this->SetY($this->img_rb_y); 13432 break; 13433 } 13434 default:{ 13435 break; 13436 } 13437 } 13438 } 13439 13440 /** 13441 * This function is DEPRECATED, please use the new write1DBarcode() function. 13442 * @param int $x x position in user units 13443 * @param int $y y position in user units 13444 * @param int $w width in user units 13445 * @param int $h height position in user units 13446 * @param string $type type of barcode (I25, C128A, C128B, C128C, C39) 13447 * @param string $style barcode style 13448 * @param string $font font for text 13449 * @param int $xres x resolution 13450 * @param string $code code to print 13451 * @deprecated deprecated since version 3.1.000 (2008-06-10) 13452 * @access public 13453 * @see write1DBarcode() 13454 */ 13455 public function writeBarcode($x, $y, $w, $h, $type, $style, $font, $xres, $code) { 13456 // convert old settings for the new write1DBarcode() function. 13457 $xres = 1 / $xres; 13458 $newstyle = array( 13459 'position' => 'L', 13460 'border' => false, 13461 'padding' => 0, 13462 'fgcolor' => array(0,0,0), 13463 'bgcolor' => false, 13464 'text' => true, 13465 'font' => $font, 13466 'fontsize' => 8, 13467 'stretchtext' => 4 13468 ); 13469 if ($style & 1) { 13470 $newstyle['border'] = true; 13471 } 13472 if ($style & 2) { 13473 $newstyle['bgcolor'] = false; 13474 } 13475 if ($style & 4) { 13476 $newstyle['position'] = 'C'; 13477 } elseif ($style & 8) { 13478 $newstyle['position'] = 'L'; 13479 } elseif ($style & 16) { 13480 $newstyle['position'] = 'R'; 13481 } 13482 if ($style & 128) { 13483 $newstyle['text'] = true; 13484 } 13485 if ($style & 256) { 13486 $newstyle['stretchtext'] = 4; 13487 } 13488 $this->write1DBarcode($code, $type, $x, $y, $w, $h, $xres, $newstyle, ''); 13489 } 13490 13491 /** 13492 * Print 2D Barcode. 13493 * @param string $code code to print 13494 * @param string $type type of barcode (see 2dbarcodes.php for supported formats). 13495 * @param int $x x position in user units 13496 * @param int $y y position in user units 13497 * @param int $w width in user units 13498 * @param int $h height in user units 13499 * @param array $style array of options:<ul><li>boolean $style['border'] if true prints a border around the barcode</li><li>int $style['padding'] padding to leave around the barcode in user units (set to 'auto' for automatic padding)</li><li>array $style['fgcolor'] color array for bars and text</li><li>mixed $style['bgcolor'] color array for background or false for transparent</li></ul> 13500 * @param string $align Indicates the alignment of the pointer next to barcode insertion relative to barcode height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul> 13501 * @author Nicola Asuni 13502 * @since 4.5.037 (2009-04-07) 13503 * @access public 13504 */ 13505 public function write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='') { 13506 if ($this->empty_string($code)) { 13507 return; 13508 } 13509 require_once(dirname(__FILE__).'/2dbarcodes.php'); 13510 // save current graphic settings 13511 $gvars = $this->getGraphicVars(); 13512 // create new barcode object 13513 $barcodeobj = new TCPDF2DBarcode($code, $type); 13514 $arrcode = $barcodeobj->getBarcodeArray(); 13515 if ($arrcode === false) { 13516 $this->Error('Error in 2D barcode string'); 13517 } 13518 // set default values 13519 if (!isset($style['fgcolor'])) { 13520 $style['fgcolor'] = array(0,0,0); // default black 13521 } 13522 if (!isset($style['bgcolor'])) { 13523 $style['bgcolor'] = false; // default transparent 13524 } 13525 if (!isset($style['border'])) { 13526 $style['border'] = false; 13527 } 13528 // set foreground color 13529 $this->SetDrawColorArray($style['fgcolor']); 13530 if ($this->empty_string($x)) { 13531 $x = $this->GetX(); 13532 } 13533 if ($this->rtl) { 13534 $x = $this->w - $x; 13535 } 13536 if ($this->empty_string($y)) { 13537 $y = $this->GetY(); 13538 } 13539 if ($this->empty_string($w) OR ($w <= 0)) { 13540 if ($this->rtl) { 13541 $w = $x - $this->lMargin; 13542 } else { 13543 $w = $this->w - $this->rMargin - $x; 13544 } 13545 } 13546 if ($this->empty_string($h) OR ($h <= 0)) { 13547 // 2d barcodes are square by default 13548 $h = $w; 13549 } 13550 $prev_x = $this->x; 13551 if ($this->checkPageBreak($h, $y)) { 13552 $y = $this->GetY() + $this->cMargin; 13553 if ($this->rtl) { 13554 $x += ($prev_x - $this->x); 13555 } else { 13556 $x += ($this->x - $prev_x); 13557 } 13558 } 13559 // padding 13560 if (!isset($style['padding'])) { 13561 $style['padding'] = 0; 13562 } elseif ($style['padding'] === 'auto') { 13563 $style['padding'] = 4 * $w / (8 + $arrcode['num_cols']); 13564 } 13565 // calculate barcode size (excluding padding) 13566 $bw = $w - (2 * $style['padding']); 13567 $bh = $h - (2 * $style['padding']); 13568 // calculate starting coordinates 13569 if ($this->rtl) { 13570 $xpos = $x - $w; 13571 } else { 13572 $xpos = $x; 13573 } 13574 $xpos += $style['padding']; 13575 $ypos = $y + $style['padding']; 13576 // barcode is always printed in LTR direction 13577 $tempRTL = $this->rtl; 13578 $this->rtl = false; 13579 // print background color 13580 if ($style['bgcolor']) { 13581 $this->Rect($x, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']); 13582 } elseif ($style['border']) { 13583 $this->Rect($x, $y, $w, $h, 'D'); 13584 } 13585 // print barcode cells 13586 if ($arrcode !== false) { 13587 $rows = $arrcode['num_rows']; 13588 $cols = $arrcode['num_cols']; 13589 // calculate dimension of single barcode cell 13590 $cw = $bw / $cols; 13591 $ch = $bh / $rows; 13592 // for each row 13593 for ($r = 0; $r < $rows; ++$r) { 13594 $xr = $xpos; 13595 // for each column 13596 for ($c = 0; $c < $cols; ++$c) { 13597 if ($arrcode['bcode'][$r][$c] == 1) { 13598 // draw a single barcode cell 13599 $this->Rect($xr, $ypos, $cw, $ch, 'F', array(), $style['fgcolor']); 13600 } 13601 $xr += $cw; 13602 } 13603 $ypos += $ch; 13604 } 13605 } 13606 // restore original direction 13607 $this->rtl = $tempRTL; 13608 // restore previous settings 13609 $this->setGraphicVars($gvars); 13610 // set bottomcoordinates 13611 $this->img_rb_y = $y + $h; 13612 if ($this->rtl) { 13613 // set left side coordinate 13614 $this->img_rb_x = ($this->w - $x - $w); 13615 } else { 13616 // set right side coordinate 13617 $this->img_rb_x = $x + $w; 13618 } 13619 // set pointer to align the successive text/objects 13620 switch($align) { 13621 case 'T':{ 13622 $this->y = $y; 13623 $this->x = $this->img_rb_x; 13624 break; 13625 } 13626 case 'M':{ 13627 $this->y = $y + round($h/2); 13628 $this->x = $this->img_rb_x; 13629 break; 13630 } 13631 case 'B':{ 13632 $this->y = $this->img_rb_y; 13633 $this->x = $this->img_rb_x; 13634 break; 13635 } 13636 case 'N':{ 13637 $this->SetY($this->img_rb_y); 13638 break; 13639 } 13640 default:{ 13641 break; 13642 } 13643 } 13644 } 13645 13646 /** 13647 * Returns an array containing current margins: 13648 * <ul> 13649 <li>$ret['left'] = left margin</li> 13650 <li>$ret['right'] = right margin</li> 13651 <li>$ret['top'] = top margin</li> 13652 <li>$ret['bottom'] = bottom margin</li> 13653 <li>$ret['header'] = header margin</li> 13654 <li>$ret['footer'] = footer margin</li> 13655 <li>$ret['cell'] = cell margin</li> 13656 * </ul> 13657 * @return array containing all margins measures 13658 * @access public 13659 * @since 3.2.000 (2008-06-23) 13660 */ 13661 public function getMargins() { 13662 $ret = array( 13663 'left' => $this->lMargin, 13664 'right' => $this->rMargin, 13665 'top' => $this->tMargin, 13666 'bottom' => $this->bMargin, 13667 'header' => $this->header_margin, 13668 'footer' => $this->footer_margin, 13669 'cell' => $this->cMargin, 13670 ); 13671 return $ret; 13672 } 13673 13674 /** 13675 * Returns an array containing original margins: 13676 * <ul> 13677 <li>$ret['left'] = left margin</li> 13678 <li>$ret['right'] = right margin</li> 13679 * </ul> 13680 * @return array containing all margins measures 13681 * @access public 13682 * @since 4.0.012 (2008-07-24) 13683 */ 13684 public function getOriginalMargins() { 13685 $ret = array( 13686 'left' => $this->original_lMargin, 13687 'right' => $this->original_rMargin 13688 ); 13689 return $ret; 13690 } 13691 13692 /** 13693 * Returns the current font size. 13694 * @return current font size 13695 * @access public 13696 * @since 3.2.000 (2008-06-23) 13697 */ 13698 public function getFontSize() { 13699 return $this->FontSize; 13700 } 13701 13702 /** 13703 * Returns the current font size in points unit. 13704 * @return current font size in points unit 13705 * @access public 13706 * @since 3.2.000 (2008-06-23) 13707 */ 13708 public function getFontSizePt() { 13709 return $this->FontSizePt; 13710 } 13711 13712 /** 13713 * Returns the current font family name. 13714 * @return string current font family name 13715 * @access public 13716 * @since 4.3.008 (2008-12-05) 13717 */ 13718 public function getFontFamily() { 13719 return $this->FontFamily; 13720 } 13721 13722 /** 13723 * Returns the current font style. 13724 * @return string current font style 13725 * @access public 13726 * @since 4.3.008 (2008-12-05) 13727 */ 13728 public function getFontStyle() { 13729 return $this->FontStyle; 13730 } 13731 13732 /** 13733 * Prints a cell (rectangular area) with optional borders, background color and html text string. 13734 * The upper-left corner of the cell corresponds to the current position. After the call, the current position moves to the right or to the next line.<br /> 13735 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting. 13736 * @param float $w Cell width. If 0, the cell extends up to the right margin. 13737 * @param float $h Cell minimum height. The cell extends automatically if needed. 13738 * @param float $x upper-left corner X coordinate 13739 * @param float $y upper-left corner Y coordinate 13740 * @param string $html html text to print. Default value: empty string. 13741 * @param mixed $border Indicates if borders must be drawn around the cell. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> 13742 * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL language)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul> 13743 Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0. 13744 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. 13745 * @param boolean $reseth if true reset the last cell height (default true). 13746 * @param string $align Allows to center or align the text. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul> 13747 * @param boolean $autopadding if true, uses internal padding and automatically adjust it to account for line width. 13748 * @access public 13749 * @uses MultiCell() 13750 * @see Multicell(), writeHTML() 13751 */ 13752 public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=0, $reseth=true, $align='', $autopadding=true) { 13753 return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true, $autopadding, 0); 13754 } 13755 13756 /** 13757 * Returns the HTML DOM array. 13758 * <ul><li>$dom[$key]['tag'] = true if tag, false otherwise;</li><li>$dom[$key]['value'] = tag name or text;</li><li>$dom[$key]['opening'] = true if opening tag, false otherwise;</li><li>$dom[$key]['attribute'] = array of attributes (attribute name is the key);</li><li>$dom[$key]['style'] = array of style attributes (attribute name is the key);</li><li>$dom[$key]['parent'] = id of parent element;</li><li>$dom[$key]['fontname'] = font family name;</li><li>$dom[$key]['fontstyle'] = font style;</li><li>$dom[$key]['fontsize'] = font size in points;</li><li>$dom[$key]['bgcolor'] = RGB array of background color;</li><li>$dom[$key]['fgcolor'] = RGB array of foreground color;</li><li>$dom[$key]['width'] = width in pixels;</li><li>$dom[$key]['height'] = height in pixels;</li><li>$dom[$key]['align'] = text alignment;</li><li>$dom[$key]['cols'] = number of colums in table;</li><li>$dom[$key]['rows'] = number of rows in table;</li></ul> 13759 * @param string $html html code 13760 * @return array 13761 * @access protected 13762 * @since 3.2.000 (2008-06-20) 13763 */ 13764 protected function getHtmlDomArray($html) { 13765 // define block tags 13766 $blocktags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table','tr','td'); 13767 // remove all unsupported tags (the line below lists all supported tags) 13768 $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><i><img><input><label><li><ol><option><p><pre><select><small><span><strong><sub><sup><table><tablehead><tcpdf><td><textarea><th><thead><tr><tt><u><ul>'); 13769 //replace some blank characters 13770 $html = preg_replace('/<pre/', '<xre', $html); // preserve pre tag 13771 $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); 13772 $html = preg_replace('@(\r\n|\r)@', "\n", $html); 13773 $repTable = array("\t" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\"); 13774 $html = strtr($html, $repTable); 13775 $offset = 0; 13776 while (($offset < strlen($html)) AND ($pos = strpos($html, '</pre>', $offset)) !== false) { 13777 $html_a = substr($html, 0, $offset); 13778 $html_b = substr($html, $offset, ($pos - $offset + 6)); 13779 while (preg_match("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", $html_b)) { 13780 // preserve newlines on <pre> tag 13781 $html_b = preg_replace("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", "<xre\\1>\\2<br />\\3</pre>", $html_b); 13782 } 13783 $html = $html_a.$html_b.substr($html, $pos + 6); 13784 $offset = strlen($html_a.$html_b); 13785 } 13786 $offset = 0; 13787 while (($offset < strlen($html)) AND ($pos = strpos($html, '</textarea>', $offset)) !== false) { 13788 $html_a = substr($html, 0, $offset); 13789 $html_b = substr($html, $offset, ($pos - $offset + 11)); 13790 while (preg_match("'<textarea([^\>]*)>(.*?)\n(.*?)</textarea>'si", $html_b)) { 13791 // preserve newlines on <textarea> tag 13792 $html_b = preg_replace("'<textarea([^\>]*)>(.*?)\n(.*?)</textarea>'si", "<textarea\\1>\\2<TBR>\\3</textarea>", $html_b); 13793 $html_b = preg_replace("'<textarea([^\>]*)>(.*?)[\"](.*?)</textarea>'si", "<textarea\\1>\\2''\\3</textarea>", $html_b); 13794 } 13795 $html = $html_a.$html_b.substr($html, $pos + 11); 13796 $offset = strlen($html_a.$html_b); 13797 } 13798 $html = preg_replace("'([\s]*)<option'si", "<option", $html); 13799 $html = preg_replace("'</option>([\s]*)'si", "</option>", $html); 13800 $offset = 0; 13801 while (($offset < strlen($html)) AND ($pos = strpos($html, '</option>', $offset)) !== false) { 13802 $html_a = substr($html, 0, $offset); 13803 $html_b = substr($html, $offset, ($pos - $offset + 9)); 13804 while (preg_match("'<option([^\>]*)>(.*?)</option>'si", $html_b)) { 13805 $html_b = preg_replace("'<option([\s]+)value=\"([^\"]*)\"([^\>]*)>(.*?)</option>'si", "\\2\t\\4\r", $html_b); 13806 $html_b = preg_replace("'<option([^\>]*)>(.*?)</option>'si", "\\2\r", $html_b); 13807 } 13808 $html = $html_a.$html_b.substr($html, $pos + 9); 13809 $offset = strlen($html_a.$html_b); 13810 } 13811 $html = preg_replace("'<select([^\>]*)>'si", "<select\\1 opt=\"", $html); 13812 $html = preg_replace("'([\s]+)</select>'si", "\" />", $html); 13813 $html = str_replace("\n", ' ', $html); 13814 // restore textarea newlines 13815 $html = str_replace('<TBR>', "\n", $html); 13816 // remove extra spaces from code 13817 $html = preg_replace('/[\s]+<\/(table|tr|td|th|ul|ol|li|dl|dt|dd)>/', '</\\1>', $html); 13818 $html = preg_replace('/[\s]+<(tr|td|th|ul|ol|li|dl|dt|dd|br)/', '<\\1', $html); 13819 $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); 13820 $html = preg_replace('/<\/(td|th)>/', '<marker style="font-size:0"/></\\1>', $html); 13821 $html = preg_replace('/<\/table>([\s]*)<marker style="font-size:0"\/>/', '</table>', $html); 13822 $html = preg_replace('/[\s]*<img/', ' <img', $html); 13823 $html = preg_replace('/<img([^\>]*)>/xi', '<img\\1><span><marker style="font-size:0"/></span>', $html); 13824 $html = preg_replace('/<xre/', '<pre', $html); // restore pre tag 13825 $html = preg_replace('/<textarea([^\>]*)>/xi', '<textarea\\1 value="', $html); 13826 $html = preg_replace('/<\/textarea>/', '" />', $html); 13827 // trim string 13828 $html = preg_replace('/^[\s]+/', '', $html); 13829 $html = preg_replace('/[\s]+$/', '', $html); 13830 // pattern for generic tag 13831 $tagpattern = '/(<[^>]+>)/'; 13832 // explodes the string 13833 $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); 13834 // count elements 13835 $maxel = count($a); 13836 $elkey = 0; 13837 $key = 0; 13838 // create an array of elements 13839 $dom = array(); 13840 $dom[$key] = array(); 13841 // set first void element 13842 $dom[$key]['tag'] = false; 13843 $dom[$key]['block'] = false; 13844 $dom[$key]['value'] = ''; 13845 $dom[$key]['parent'] = 0; 13846 $dom[$key]['fontname'] = $this->FontFamily; 13847 $dom[$key]['fontstyle'] = $this->FontStyle; 13848 $dom[$key]['fontsize'] = $this->FontSizePt; 13849 $dom[$key]['stroke'] = $this->textstrokewidth; 13850 $dom[$key]['fill'] = (($this->textrendermode % 2) == 0); 13851 $dom[$key]['clip'] = ($this->textrendermode > 3); 13852 $dom[$key]['line-height'] = $this->cell_height_ratio; 13853 $dom[$key]['bgcolor'] = false; 13854 $dom[$key]['fgcolor'] = $this->fgcolor; 13855 $dom[$key]['strokecolor'] = $this->strokecolor; 13856 $dom[$key]['align'] = ''; 13857 $dom[$key]['listtype'] = ''; 13858 $dom[$key]['text-indent'] = 0; 13859 $thead = false; // true when we are inside the THEAD tag 13860 ++$key; 13861 $level = array(); 13862 array_push($level, 0); // root 13863 while ($elkey < $maxel) { 13864 $dom[$key] = array(); 13865 $element = $a[$elkey]; 13866 $dom[$key]['elkey'] = $elkey; 13867 if (preg_match($tagpattern, $element)) { 13868 // html tag 13869 $element = substr($element, 1, -1); 13870 // get tag name 13871 preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag); 13872 $tagname = strtolower($tag[1]); 13873 // check if we are inside a table header 13874 if ($tagname == 'thead') { 13875 if ($element{0} == '/') { 13876 $thead = false; 13877 } else { 13878 $thead = true; 13879 } 13880 ++$elkey; 13881 continue; 13882 } 13883 $dom[$key]['tag'] = true; 13884 $dom[$key]['value'] = $tagname; 13885 if (in_array($dom[$key]['value'], $blocktags)) { 13886 $dom[$key]['block'] = true; 13887 } else { 13888 $dom[$key]['block'] = false; 13889 } 13890 if ($element{0} == '/') { 13891 // *** closing html tag 13892 $dom[$key]['opening'] = false; 13893 $dom[$key]['parent'] = end($level); 13894 array_pop($level); 13895 $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname']; 13896 $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle']; 13897 $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize']; 13898 $dom[$key]['stroke'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['stroke']; 13899 $dom[$key]['fill'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fill']; 13900 $dom[$key]['clip'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['clip']; 13901 $dom[$key]['line-height'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['line-height']; 13902 $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor']; 13903 $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor']; 13904 $dom[$key]['strokecolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['strokecolor']; 13905 $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align']; 13906 if (isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'])) { 13907 $dom[$key]['listtype'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype']; 13908 } 13909 // set the number of columns in table tag 13910 if (($dom[$key]['value'] == 'tr') AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) { 13911 $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols']; 13912 } 13913 if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) { 13914 $dom[($dom[$key]['parent'])]['content'] = ''; 13915 for ($i = ($dom[$key]['parent'] + 1); $i < $key; ++$i) { 13916 $dom[($dom[$key]['parent'])]['content'] .= $a[$dom[$i]['elkey']]; 13917 } 13918 $key = $i; 13919 // mark nested tables 13920 $dom[($dom[$key]['parent'])]['content'] = str_replace('<table', '<table nested="true"', $dom[($dom[$key]['parent'])]['content']); 13921 // remove thead sections from nested tables 13922 $dom[($dom[$key]['parent'])]['content'] = str_replace('<thead>', '', $dom[($dom[$key]['parent'])]['content']); 13923 $dom[($dom[$key]['parent'])]['content'] = str_replace('</thead>', '', $dom[($dom[$key]['parent'])]['content']); 13924 } 13925 // store header rows on a new table 13926 if (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['thead'] === true)) { 13927 if ($this->empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) { 13928 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']]; 13929 } 13930 for ($i = $dom[$key]['parent']; $i <= $key; ++$i) { 13931 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']]; 13932 } 13933 if (!isset($dom[($dom[$key]['parent'])]['attribute'])) { 13934 $dom[($dom[$key]['parent'])]['attribute'] = array(); 13935 } 13936 // header elements must be always contained in a single page 13937 $dom[($dom[$key]['parent'])]['attribute']['nobr'] = 'true'; 13938 } 13939 if (($dom[$key]['value'] == 'table') AND (!$this->empty_string($dom[($dom[$key]['parent'])]['thead']))) { 13940 // remove the nobr attributes from the table header 13941 $dom[($dom[$key]['parent'])]['thead'] = str_replace(' nobr="true"', '', $dom[($dom[$key]['parent'])]['thead']); 13942 $dom[($dom[$key]['parent'])]['thead'] .= '</tablehead>'; 13943 } 13944 } else { 13945 // *** opening html tag 13946 $dom[$key]['opening'] = true; 13947 $dom[$key]['parent'] = end($level); 13948 if (substr($element, -1, 1) != '/') { 13949 // not self-closing tag 13950 array_push($level, $key); 13951 $dom[$key]['self'] = false; 13952 } else { 13953 $dom[$key]['self'] = true; 13954 } 13955 // copy some values from parent 13956 $parentkey = 0; 13957 if ($key > 0) { 13958 $parentkey = $dom[$key]['parent']; 13959 $dom[$key]['fontname'] = $dom[$parentkey]['fontname']; 13960 $dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle']; 13961 $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize']; 13962 $dom[$key]['stroke'] = $dom[$parentkey]['stroke']; 13963 $dom[$key]['fill'] = $dom[$parentkey]['fill']; 13964 $dom[$key]['clip'] = $dom[$parentkey]['clip']; 13965 $dom[$key]['line-height'] = $dom[$parentkey]['line-height']; 13966 $dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor']; 13967 $dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor']; 13968 $dom[$key]['strokecolor'] = $dom[$parentkey]['strokecolor']; 13969 $dom[$key]['align'] = $dom[$parentkey]['align']; 13970 $dom[$key]['listtype'] = $dom[$parentkey]['listtype']; 13971 $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent']; 13972 } 13973 // get attributes 13974 preg_match_all('/([^=\s]*)=["]?([^"]*)["]?/', $element, $attr_array, PREG_PATTERN_ORDER); 13975 $dom[$key]['attribute'] = array(); // reset attribute array 13976 while (list($id, $name) = each($attr_array[1])) { 13977 $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id]; 13978 } 13979 // split style attributes 13980 if (isset($dom[$key]['attribute']['style'])) { 13981 // get style attributes 13982 preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER); 13983 $dom[$key]['style'] = array(); // reset style attribute array 13984 while (list($id, $name) = each($style_array[1])) { 13985 $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]); 13986 } 13987 // --- get some style attributes --- 13988 if (isset($dom[$key]['style']['font-family'])) { 13989 // font family 13990 if (isset($dom[$key]['style']['font-family'])) { 13991 $fontslist = preg_split('/[,]/', strtolower($dom[$key]['style']['font-family'])); 13992 foreach ($fontslist as $font) { 13993 $font = trim(strtolower($font)); 13994 if (in_array($font, $this->fontlist) OR in_array($font, $this->fontkeys)) { 13995 $dom[$key]['fontname'] = $font; 13996 break; 13997 } 13998 } 13999 } 14000 } 14001 // list-style-type 14002 if (isset($dom[$key]['style']['list-style-type'])) { 14003 $dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type'])); 14004 if ($dom[$key]['listtype'] == 'inherit') { 14005 $dom[$key]['listtype'] = $dom[$parentkey]['listtype']; 14006 } 14007 } 14008 // text-indent 14009 if (isset($dom[$key]['style']['text-indent'])) { 14010 $dom[$key]['text-indent'] = $this->getHTMLUnitToUnits($dom[$key]['style']['text-indent']); 14011 if ($dom[$key]['text-indent'] == 'inherit') { 14012 $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent']; 14013 } 14014 } 14015 // font size 14016 if (isset($dom[$key]['style']['font-size'])) { 14017 $fsize = trim($dom[$key]['style']['font-size']); 14018 switch ($fsize) { 14019 // absolute-size 14020 case 'xx-small': { 14021 $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 4; 14022 break; 14023 } 14024 case 'x-small': { 14025 $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 3; 14026 break; 14027 } 14028 case 'small': { 14029 $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 2; 14030 break; 14031 } 14032 case 'medium': { 14033 $dom[$key]['fontsize'] = $dom[0]['fontsize']; 14034 break; 14035 } 14036 case 'large': { 14037 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 2; 14038 break; 14039 } 14040 case 'x-large': { 14041 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 4; 14042 break; 14043 } 14044 case 'xx-large': { 14045 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 6; 14046 break; 14047 } 14048 // relative-size 14049 case 'smaller': { 14050 $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] - 3; 14051 break; 14052 } 14053 case 'larger': { 14054 $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] + 3; 14055 break; 14056 } 14057 default: { 14058 $dom[$key]['fontsize'] = $this->getHTMLUnitToUnits($fsize, $dom[$parentkey]['fontsize'], 'pt', true); 14059 } 14060 } 14061 } 14062 // line-height 14063 if (isset($dom[$key]['style']['line-height'])) { 14064 $lineheight = trim($dom[$key]['style']['line-height']); 14065 switch ($lineheight) { 14066 // A normal line height. This is default 14067 case 'normal': { 14068 $dom[$key]['line-height'] = $dom[0]['line-height']; 14069 break; 14070 } 14071 default: { 14072 if (is_numeric($lineheight)) { 14073 $lineheight = $lineheight * 100; 14074 } 14075 $dom[$key]['line-height'] = $this->getHTMLUnitToUnits($lineheight, 1, '%', true); 14076 } 14077 } 14078 } 14079 // font style 14080 if (isset($dom[$key]['style']['font-weight']) AND (strtolower($dom[$key]['style']['font-weight']{0}) == 'b')) { 14081 $dom[$key]['fontstyle'] .= 'B'; 14082 } 14083 if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style']{0}) == 'i')) { 14084 $dom[$key]['fontstyle'] .= 'I'; 14085 } 14086 // font color 14087 if (isset($dom[$key]['style']['color']) AND (!$this->empty_string($dom[$key]['style']['color']))) { 14088 $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['color']); 14089 } 14090 // background color 14091 if (isset($dom[$key]['style']['background-color']) AND (!$this->empty_string($dom[$key]['style']['background-color']))) { 14092 $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['background-color']); 14093 } 14094 // text-decoration 14095 if (isset($dom[$key]['style']['text-decoration'])) { 14096 $decors = explode(' ', strtolower($dom[$key]['style']['text-decoration'])); 14097 foreach ($decors as $dec) { 14098 $dec = trim($dec); 14099 if (!$this->empty_string($dec)) { 14100 if ($dec{0} == 'u') { 14101 // underline 14102 $dom[$key]['fontstyle'] .= 'U'; 14103 } elseif ($dec{0} == 'l') { 14104 // line-trough 14105 $dom[$key]['fontstyle'] .= 'D'; 14106 } elseif ($dec{0} == 'o') { 14107 // overline 14108 $dom[$key]['fontstyle'] .= 'O'; 14109 } 14110 } 14111 } 14112 } 14113 // check for width attribute 14114 if (isset($dom[$key]['style']['width'])) { 14115 $dom[$key]['width'] = $dom[$key]['style']['width']; 14116 } 14117 // check for height attribute 14118 if (isset($dom[$key]['style']['height'])) { 14119 $dom[$key]['height'] = $dom[$key]['style']['height']; 14120 } 14121 // check for text alignment 14122 if (isset($dom[$key]['style']['text-align'])) { 14123 $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align']{0}); 14124 } 14125 // check for border attribute 14126 if (isset($dom[$key]['style']['border'])) { 14127 $dom[$key]['attribute']['border'] = $dom[$key]['style']['border']; 14128 } 14129 // page-break-inside 14130 if (isset($dom[$key]['style']['page-break-inside']) AND ($dom[$key]['style']['page-break-inside'] == 'avoid')) { 14131 $dom[$key]['attribute']['nobr'] = 'true'; 14132 } 14133 // page-break-before 14134 if (isset($dom[$key]['style']['page-break-before'])) { 14135 if ($dom[$key]['style']['page-break-before'] == 'always') { 14136 $dom[$key]['attribute']['pagebreak'] = 'true'; 14137 } elseif ($dom[$key]['style']['page-break-before'] == 'left') { 14138 $dom[$key]['attribute']['pagebreak'] = 'left'; 14139 } elseif ($dom[$key]['style']['page-break-before'] == 'right') { 14140 $dom[$key]['attribute']['pagebreak'] = 'right'; 14141 } 14142 } 14143 // page-break-after 14144 if (isset($dom[$key]['style']['page-break-after'])) { 14145 if ($dom[$key]['style']['page-break-after'] == 'always') { 14146 $dom[$key]['attribute']['pagebreakafter'] = 'true'; 14147 } elseif ($dom[$key]['style']['page-break-after'] == 'left') { 14148 $dom[$key]['attribute']['pagebreakafter'] = 'left'; 14149 } elseif ($dom[$key]['style']['page-break-after'] == 'right') { 14150 $dom[$key]['attribute']['pagebreakafter'] = 'right'; 14151 } 14152 } 14153 } 14154 // check for font tag 14155 if ($dom[$key]['value'] == 'font') { 14156 // font family 14157 if (isset($dom[$key]['attribute']['face'])) { 14158 $fontslist = preg_split('/[,]/', strtolower($dom[$key]['attribute']['face'])); 14159 foreach ($fontslist as $font) { 14160 $font = trim(strtolower($font)); 14161 if (in_array($font, $this->fontlist) OR in_array($font, $this->fontkeys)) { 14162 $dom[$key]['fontname'] = $font; 14163 break; 14164 } 14165 } 14166 } 14167 // font size 14168 if (isset($dom[$key]['attribute']['size'])) { 14169 if ($key > 0) { 14170 if ($dom[$key]['attribute']['size']{0} == '+') { 14171 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1)); 14172 } elseif ($dom[$key]['attribute']['size']{0} == '-') { 14173 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1)); 14174 } else { 14175 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']); 14176 } 14177 } else { 14178 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']); 14179 } 14180 } 14181 } 14182 // force natural alignment for lists 14183 if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl')) 14184 AND (!isset($dom[$key]['align']) OR $this->empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) { 14185 if ($this->rtl) { 14186 $dom[$key]['align'] = 'R'; 14187 } else { 14188 $dom[$key]['align'] = 'L'; 14189 } 14190 } 14191 if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) { 14192 $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO; 14193 } 14194 if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) { 14195 $dom[$key]['fontstyle'] .= 'B'; 14196 } 14197 if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) { 14198 $dom[$key]['fontstyle'] .= 'I'; 14199 } 14200 if ($dom[$key]['value'] == 'u') { 14201 $dom[$key]['fontstyle'] .= 'U'; 14202 } 14203 if ($dom[$key]['value'] == 'del') { 14204 $dom[$key]['fontstyle'] .= 'D'; 14205 } 14206 if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) { 14207 $dom[$key]['fontname'] = $this->default_monospaced_font; 14208 } 14209 if (($dom[$key]['value']{0} == 'h') AND (intval($dom[$key]['value']{1}) > 0) AND (intval($dom[$key]['value']{1}) < 7)) { 14210 $headsize = (4 - intval($dom[$key]['value']{1})) * 2; 14211 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize; 14212 $dom[$key]['fontstyle'] .= 'B'; 14213 } 14214 if (($dom[$key]['value'] == 'table')) { 14215 $dom[$key]['rows'] = 0; // number of rows 14216 $dom[$key]['trids'] = array(); // IDs of TR elements 14217 $dom[$key]['thead'] = ''; // table header rows 14218 } 14219 if (($dom[$key]['value'] == 'tr')) { 14220 $dom[$key]['cols'] = 0; 14221 if ($thead) { 14222 $dom[$key]['thead'] = true; 14223 // rows on thead block are printed as a separate table 14224 } else { 14225 $dom[$key]['thead'] = false; 14226 // store the number of rows on table element 14227 ++$dom[($dom[$key]['parent'])]['rows']; 14228 // store the TR elements IDs on table element 14229 array_push($dom[($dom[$key]['parent'])]['trids'], $key); 14230 } 14231 } 14232 if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) { 14233 if (isset($dom[$key]['attribute']['colspan'])) { 14234 $colspan = intval($dom[$key]['attribute']['colspan']); 14235 } else { 14236 $colspan = 1; 14237 } 14238 $dom[$key]['attribute']['colspan'] = $colspan; 14239 $dom[($dom[$key]['parent'])]['cols'] += $colspan; 14240 } 14241 // set foreground color attribute 14242 if (isset($dom[$key]['attribute']['color']) AND (!$this->empty_string($dom[$key]['attribute']['color']))) { 14243 $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['color']); 14244 } 14245 // set background color attribute 14246 if (isset($dom[$key]['attribute']['bgcolor']) AND (!$this->empty_string($dom[$key]['attribute']['bgcolor']))) { 14247 $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['bgcolor']); 14248 } 14249 // set stroke color attribute 14250 if (isset($dom[$key]['attribute']['strokecolor']) AND (!$this->empty_string($dom[$key]['attribute']['strokecolor']))) { 14251 $dom[$key]['strokecolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['strokecolor']); 14252 } 14253 // check for width attribute 14254 if (isset($dom[$key]['attribute']['width'])) { 14255 $dom[$key]['width'] = $dom[$key]['attribute']['width']; 14256 } 14257 // check for height attribute 14258 if (isset($dom[$key]['attribute']['height'])) { 14259 $dom[$key]['height'] = $dom[$key]['attribute']['height']; 14260 } 14261 // check for text alignment 14262 if (isset($dom[$key]['attribute']['align']) AND (!$this->empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) { 14263 $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align']{0}); 14264 } 14265 // check for text rendering mode (the following attributes do not exist in HTML) 14266 if (isset($dom[$key]['attribute']['stroke'])) { 14267 // font stroke width 14268 $dom[$key]['stroke'] = $this->getHTMLUnitToUnits($dom[$key]['attribute']['stroke'], $dom[$key]['fontsize'], 'pt', true); 14269 } 14270 if (isset($dom[$key]['attribute']['fill'])) { 14271 // font fill 14272 if ($dom[$key]['attribute']['fill'] == 'true') { 14273 $dom[$key]['fill'] = true; 14274 } else { 14275 $dom[$key]['fill'] = false; 14276 } 14277 } 14278 if (isset($dom[$key]['attribute']['clip'])) { 14279 // clipping mode 14280 if ($dom[$key]['attribute']['clip'] == 'true') { 14281 $dom[$key]['clip'] = true; 14282 } else { 14283 $dom[$key]['clip'] = false; 14284 } 14285 } 14286 } // end opening tag 14287 } else { 14288 // text 14289 $dom[$key]['tag'] = false; 14290 $dom[$key]['block'] = false; 14291 $dom[$key]['value'] = stripslashes($this->unhtmlentities($element)); 14292 $dom[$key]['parent'] = end($level); 14293 } 14294 ++$elkey; 14295 ++$key; 14296 } 14297 return $dom; 14298 } 14299 14300 /** 14301 * Returns the string used to find spaces 14302 * @return string 14303 * @access protected 14304 * @author Nicola Asuni 14305 * @since 4.8.024 (2010-01-15) 14306 */ 14307 protected function getSpaceString() { 14308 $spacestr = chr(32); 14309 if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) { 14310 $spacestr = chr(0).chr(32); 14311 } 14312 return $spacestr; 14313 } 14314 14315 /** 14316 * Allows to preserve some HTML formatting (limited support).<br /> 14317 * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting. 14318 * Supported tags are: a, b, blockquote, br, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, img, li, ol, p, pre, small, span, strong, sub, sup, table, tcpdf, td, th, thead, tr, tt, u, ul 14319 * @param string $html text to display 14320 * @param boolean $ln if true add a new line after text (default = true) 14321 * @param int $fill Indicates if the background must be painted (true) or transparent (false). 14322 * @param boolean $reseth if true reset the last cell height (default false). 14323 * @param boolean $cell if true add the default cMargin space to each Write (default false). 14324 * @param string $align Allows to center or align the text. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul> 14325 * @access public 14326 */ 14327 public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') { 14328 $gvars = $this->getGraphicVars(); 14329 // store current values 14330 $prevPage = $this->page; 14331 $prevlMargin = $this->lMargin; 14332 $prevrMargin = $this->rMargin; 14333 $curfontname = $this->FontFamily; 14334 $curfontstyle = $this->FontStyle; 14335 $curfontsize = $this->FontSizePt; 14336 $curfontascent = $this->getFontAscent($curfontname, $curfontstyle, $curfontsize); 14337 $curfontdescent = $this->getFontDescent($curfontname, $curfontstyle, $curfontsize); 14338 $this->newline = true; 14339 $startlinepage = $this->page; 14340 $minstartliney = $this->y; 14341 $maxbottomliney = 0; 14342 $startlinex = $this->x; 14343 $startliney = $this->y; 14344 $yshift = 0; 14345 $newline = true; 14346 $loop = 0; 14347 $curpos = 0; 14348 $this_method_vars = array(); 14349 $undo = false; 14350 $fontaligned = false; 14351 $this->premode = false; 14352 if (isset($this->PageAnnots[$this->page])) { 14353 $pask = count($this->PageAnnots[$this->page]); 14354 } else { 14355 $pask = 0; 14356 } 14357 if (!$this->InFooter) { 14358 if (isset($this->footerlen[$this->page])) { 14359 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page]; 14360 } else { 14361 $this->footerpos[$this->page] = $this->pagelen[$this->page]; 14362 } 14363 $startlinepos = $this->footerpos[$this->page]; 14364 } else { 14365 $startlinepos = $this->pagelen[$this->page]; 14366 } 14367 $lalign = $align; 14368 $plalign = $align; 14369 if ($this->rtl) { 14370 $w = $this->x - $this->lMargin; 14371 } else { 14372 $w = $this->w - $this->rMargin - $this->x; 14373 } 14374 $w -= (2 * $this->cMargin); 14375 if ($cell) { 14376 if ($this->rtl) { 14377 $this->x -= $this->cMargin; 14378 } else { 14379 $this->x += $this->cMargin; 14380 } 14381 } 14382 if ($this->customlistindent >= 0) { 14383 $this->listindent = $this->customlistindent; 14384 } else { 14385 $this->listindent = $this->GetStringWidth('0000'); 14386 } 14387 $this->listindentlevel = 0; 14388 // save previous states 14389 $prev_cell_height_ratio = $this->cell_height_ratio; 14390 $prev_listnum = $this->listnum; 14391 $prev_listordered = $this->listordered; 14392 $prev_listcount = $this->listcount; 14393 $prev_lispacer = $this->lispacer; 14394 $this->listnum = 0; 14395 $this->listordered = array(); 14396 $this->listcount = array(); 14397 $this->lispacer = ''; 14398 if (($this->empty_string($this->lasth)) OR ($reseth)) { 14399 //set row height 14400 $this->lasth = $this->FontSize * $this->cell_height_ratio; 14401 } 14402 $dom = $this->getHtmlDomArray($html); 14403 $maxel = count($dom); 14404 $key = 0; 14405 while ($key < $maxel) { 14406 if ($dom[$key]['tag'] AND isset($dom[$key]['attribute']['pagebreak'])) { 14407 // check for pagebreak 14408 if (($dom[$key]['attribute']['pagebreak'] == 'true') OR ($dom[$key]['attribute']['pagebreak'] == 'left') OR ($dom[$key]['attribute']['pagebreak'] == 'right')) { 14409 // add a page (or trig AcceptPageBreak() for multicolumn mode) 14410 $this->checkPageBreak($this->PageBreakTrigger + 1); 14411 } 14412 if ((($dom[$key]['attribute']['pagebreak'] == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0)))) 14413 OR (($dom[$key]['attribute']['pagebreak'] == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) { 14414 // add a page (or trig AcceptPageBreak() for multicolumn mode) 14415 $this->checkPageBreak($this->PageBreakTrigger + 1); 14416 } 14417 } 14418 if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND isset($dom[$key]['attribute']['nobr']) AND ($dom[$key]['attribute']['nobr'] == 'true')) { 14419 if (isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) { 14420 $dom[$key]['attribute']['nobr'] = false; 14421 } else { 14422 // store current object 14423 $this->startTransaction(); 14424 // save this method vars 14425 $this_method_vars['html'] = $html; 14426 $this_method_vars['ln'] = $ln; 14427 $this_method_vars['fill'] = $fill; 14428 $this_method_vars['reseth'] = $reseth; 14429 $this_method_vars['cell'] = $cell; 14430 $this_method_vars['align'] = $align; 14431 $this_method_vars['gvars'] = $gvars; 14432 $this_method_vars['prevPage'] = $prevPage; 14433 $this_method_vars['prevlMargin'] = $prevlMargin; 14434 $this_method_vars['prevrMargin'] = $prevrMargin; 14435 $this_method_vars['curfontname'] = $curfontname; 14436 $this_method_vars['curfontstyle'] = $curfontstyle; 14437 $this_method_vars['curfontsize'] = $curfontsize; 14438 $this_method_vars['curfontascent'] = $curfontascent; 14439 $this_method_vars['curfontdescent'] = $curfontdescent; 14440 $this_method_vars['minstartliney'] = $minstartliney; 14441 $this_method_vars['maxbottomliney'] = $maxbottomliney; 14442 $this_method_vars['yshift'] = $yshift; 14443 $this_method_vars['startlinepage'] = $startlinepage; 14444 $this_method_vars['startlinepos'] = $startlinepos; 14445 $this_method_vars['startlinex'] = $startlinex; 14446 $this_method_vars['startliney'] = $startliney; 14447 $this_method_vars['newline'] = $newline; 14448 $this_method_vars['loop'] = $loop; 14449 $this_method_vars['curpos'] = $curpos; 14450 $this_method_vars['pask'] = $pask; 14451 $this_method_vars['lalign'] = $lalign; 14452 $this_method_vars['plalign'] = $plalign; 14453 $this_method_vars['w'] = $w; 14454 $this_method_vars['prev_cell_height_ratio'] = $prev_cell_height_ratio; 14455 $this_method_vars['prev_listnum'] = $prev_listnum; 14456 $this_method_vars['prev_listordered'] = $prev_listordered; 14457 $this_method_vars['prev_listcount'] = $prev_listcount; 14458 $this_method_vars['prev_lispacer'] = $prev_lispacer; 14459 $this_method_vars['fontaligned'] = $fontaligned; 14460 $this_method_vars['key'] = $key; 14461 $this_method_vars['dom'] = $dom; 14462 } 14463 } 14464 // print THEAD block 14465 if (($dom[$key]['value'] == 'tr') AND isset($dom[$key]['thead']) AND $dom[$key]['thead']) { 14466 if (isset($dom[$key]['parent']) AND isset($dom[$dom[$key]['parent']]['thead']) AND !$this->empty_string($dom[$dom[$key]['parent']]['thead'])) { 14467 $this->inthead = true; 14468 // print table header (thead) 14469 $this->writeHTML($this->thead, false, false, false, false, ''); 14470 if (($this->start_transaction_page == ($this->numpages - 1)) OR ($this->y < $this->start_transaction_y) OR ($this->checkPageBreak($this->lasth, '', false))) { 14471 // restore previous object 14472 $this->rollbackTransaction(true); 14473 // restore previous values 14474 foreach ($this_method_vars as $vkey => $vval) { 14475 $$vkey = $vval; 14476 } 14477 // add a page (or trig AcceptPageBreak() for multicolumn mode) 14478 $pre_y = $this->y; 14479 if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) { 14480 // fix for multicolumn mode 14481 $startliney = $this->y; 14482 } 14483 $this->start_transaction_page = $this->page; 14484 $this->start_transaction_y = $this->y; 14485 } 14486 } 14487 // move $key index forward to skip THEAD block 14488 while ( ($key < $maxel) AND (!( 14489 ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'tr') AND (!isset($dom[$key]['thead']) OR !$dom[$key]['thead'])) 14490 OR ($dom[$key]['tag'] AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == 'table'))) )) { 14491 ++$key; 14492 } 14493 } 14494 if ($dom[$key]['tag'] OR ($key == 0)) { 14495 if (isset($dom[$key]['line-height'])) { 14496 // set line height 14497 $this->cell_height_ratio = $dom[$key]['line-height']; 14498 $this->lasth = $this->FontSize * $this->cell_height_ratio; 14499 } 14500 if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) { 14501 $dom[$key]['align'] = ($this->rtl) ? 'R' : 'L'; 14502 } 14503 // vertically align image in line 14504 if ((!$this->newline) AND ($dom[$key]['value'] == 'img') AND (isset($dom[$key]['attribute']['height'])) AND ($dom[$key]['attribute']['height'] > 0)) { 14505 // get image height 14506 $imgh = $this->getHTMLUnitToUnits($dom[$key]['attribute']['height'], $this->lasth, 'px'); 14507 // check for automatic line break 14508 $autolinebreak = false; 14509 if (isset($dom[$key]['attribute']['width']) AND ($dom[$key]['attribute']['width'] > 0)) { 14510 $imgw = $this->getHTMLUnitToUnits($dom[$key]['attribute']['width'], 1, 'px', false); 14511 if (($this->rtl AND (($this->x - $imgw) < ($this->lMargin + $this->cMargin))) 14512 OR (!$this->rtl AND (($this->x + $imgw) > ($this->w - $this->rMargin - $this->cMargin)))) { 14513 // add automatic line break 14514 $autolinebreak = true; 14515 $this->Ln('', $cell); 14516 // go back to evaluate this line break 14517 --$key; 14518 } 14519 } 14520 if (!$autolinebreak) { 14521 if (!$this->InFooter) { 14522 $pre_y = $this->y; 14523 // check for page break 14524 if ((!$this->checkPageBreak($imgh)) AND ($this->y < $pre_y)) { 14525 // fix for multicolumn mode 14526 $startliney = $this->y; 14527 } 14528 } 14529 if ($this->page > $startlinepage) { 14530 // fix line splitted over two pages 14531 if (isset($this->footerlen[$startlinepage])) { 14532 $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 14533 } 14534 // line to be moved one page forward 14535 $pagebuff = $this->getPageBuffer($startlinepage); 14536 $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos)); 14537 $tstart = substr($pagebuff, 0, $startlinepos); 14538 $tend = substr($this->getPageBuffer($startlinepage), $curpos); 14539 // remove line from previous page 14540 $this->setPageBuffer($startlinepage, $tstart.''.$tend); 14541 $pagebuff = $this->getPageBuffer($this->page); 14542 $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]); 14543 $tend = substr($pagebuff, $this->cntmrk[$this->page]); 14544 // add line start to current page 14545 $yshift = $minstartliney - $this->y; 14546 if ($fontaligned) { 14547 $yshift += ($curfontsize / $this->k); 14548 } 14549 $try = sprintf('1 0 0 1 0 %.3F cm', ($yshift * $this->k)); 14550 $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend); 14551 // shift the annotations and links 14552 if (isset($this->PageAnnots[$this->page])) { 14553 $next_pask = count($this->PageAnnots[$this->page]); 14554 } else { 14555 $next_pask = 0; 14556 } 14557 if (isset($this->PageAnnots[$startlinepage])) { 14558 foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) { 14559 if ($pak >= $pask) { 14560 $this->PageAnnots[$this->page][] = $pac; 14561 unset($this->PageAnnots[$startlinepage][$pak]); 14562 $npak = count($this->PageAnnots[$this->page]) - 1; 14563 $this->PageAnnots[$this->page][$npak]['y'] -= $yshift; 14564 } 14565 } 14566 } 14567 $pask = $next_pask; 14568 $startlinepos = $this->cntmrk[$this->page]; 14569 $startlinepage = $this->page; 14570 $startliney = $this->y; 14571 } 14572 $this->y += ((($curfontsize * $this->cell_height_ratio / $this->k) + $curfontascent - $curfontdescent) / 2) - $imgh; 14573 $minstartliney = min($this->y, $minstartliney); 14574 $maxbottomliney = ($startliney + ($this->FontSize * $this->cell_height_ratio)); 14575 } 14576 } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize'])) { 14577 // account for different font size 14578 $pfontname = $curfontname; 14579 $pfontstyle = $curfontstyle; 14580 $pfontsize = $curfontsize; 14581 $fontname = isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname; 14582 $fontstyle = isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle; 14583 $fontsize = isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize; 14584 $fontascent = $this->getFontAscent($fontname, $fontstyle, $fontsize); 14585 $fontdescent = $this->getFontDescent($fontname, $fontstyle, $fontsize); 14586 if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)) { 14587 if (is_numeric($fontsize) AND ($fontsize >= 0) 14588 AND is_numeric($curfontsize) AND ($curfontsize >= 0) 14589 AND ($fontsize != $curfontsize) AND (!$this->newline) 14590 AND ($key < ($maxel - 1)) 14591 ) { 14592 if ((!$this->newline) AND ($this->page > $startlinepage)) { 14593 // fix lines splitted over two pages 14594 if (isset($this->footerlen[$startlinepage])) { 14595 $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 14596 } 14597 // line to be moved one page forward 14598 $pagebuff = $this->getPageBuffer($startlinepage); 14599 $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos)); 14600 $tstart = substr($pagebuff, 0, $startlinepos); 14601 $tend = substr($this->getPageBuffer($startlinepage), $curpos); 14602 // remove line start from previous page 14603 $this->setPageBuffer($startlinepage, $tstart.''.$tend); 14604 $pagebuff = $this->getPageBuffer($this->page); 14605 $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]); 14606 $tend = substr($pagebuff, $this->cntmrk[$this->page]); 14607 // add line start to current page 14608 $yshift = $minstartliney - $this->y; 14609 $try = sprintf('1 0 0 1 0 %.3F cm', ($yshift * $this->k)); 14610 $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend); 14611 // shift the annotations and links 14612 if (isset($this->PageAnnots[$this->page])) { 14613 $next_pask = count($this->PageAnnots[$this->page]); 14614 } else { 14615 $next_pask = 0; 14616 } 14617 if (isset($this->PageAnnots[$startlinepage])) { 14618 foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) { 14619 if ($pak >= $pask) { 14620 $this->PageAnnots[$this->page][] = $pac; 14621 unset($this->PageAnnots[$startlinepage][$pak]); 14622 $npak = count($this->PageAnnots[$this->page]) - 1; 14623 $this->PageAnnots[$this->page][$npak]['y'] -= $yshift; 14624 } 14625 } 14626 } 14627 $pask = $next_pask; 14628 $startlinepos = $this->cntmrk[$this->page]; 14629 $startlinepage = $this->page; 14630 $startliney = $this->y; 14631 } 14632 if (!$dom[$key]['block']) { 14633 $this->y += ((($curfontsize - $fontsize) * $this->cell_height_ratio / $this->k) + $curfontascent - $fontascent - $curfontdescent + $fontdescent) / 2; 14634 $minstartliney = min($this->y, $minstartliney); 14635 $maxbottomliney = max(($this->y + (($fontsize * $this->cell_height_ratio) / $this->k)), $maxbottomliney); 14636 } 14637 $fontaligned = true; 14638 } 14639 $this->SetFont($fontname, $fontstyle, $fontsize); 14640 $this->lasth = $this->FontSize * $this->cell_height_ratio; 14641 $curfontname = $fontname; 14642 $curfontstyle = $fontstyle; 14643 $curfontsize = $fontsize; 14644 $curfontascent = $fontascent; 14645 $curfontdescent = $fontdescent; 14646 } 14647 } 14648 // set text rendering mode 14649 $textstroke = isset($dom[$key]['stroke']) ? $dom[$key]['stroke'] : $this->textstrokewidth; 14650 $textfill = isset($dom[$key]['fill']) ? $dom[$key]['fill'] : (($this->textrendermode % 2) == 0); 14651 $textclip = isset($dom[$key]['clip']) ? $dom[$key]['clip'] : ($this->textrendermode > 3); 14652 $this->setTextRenderingMode($textstroke, $textfill, $textclip); 14653 if (($plalign == 'J') AND $dom[$key]['block']) { 14654 $plalign = ''; 14655 } 14656 // get current position on page buffer 14657 $curpos = $this->pagelen[$startlinepage]; 14658 if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) { 14659 $this->SetFillColorArray($dom[$key]['bgcolor']); 14660 $wfill = true; 14661 } else { 14662 $wfill = $fill | false; 14663 } 14664 if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) { 14665 $this->SetTextColorArray($dom[$key]['fgcolor']); 14666 } 14667 if (isset($dom[$key]['strokecolor']) AND ($dom[$key]['strokecolor'] !== false)) { 14668 $this->SetDrawColorArray($dom[$key]['strokecolor']); 14669 } 14670 if (isset($dom[$key]['align'])) { 14671 $lalign = $dom[$key]['align']; 14672 } 14673 if ($this->empty_string($lalign)) { 14674 $lalign = $align; 14675 } 14676 } 14677 // align lines 14678 if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) { 14679 $newline = true; 14680 $fontaligned = false; 14681 // we are at the beginning of a new line 14682 if (isset($startlinex)) { 14683 $yshift = $minstartliney - $startliney; 14684 if (($yshift > 0) OR ($this->page > $startlinepage)) { 14685 $yshift = 0; 14686 } 14687 $t_x = 0; 14688 // the last line must be shifted to be aligned as requested 14689 $linew = abs($this->endlinex - $startlinex); 14690 $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos); 14691 if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) { 14692 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 14693 $midpos = min($opentagpos, $this->footerpos[$startlinepage]); 14694 } elseif (isset($opentagpos)) { 14695 $midpos = $opentagpos; 14696 } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) { 14697 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 14698 $midpos = $this->footerpos[$startlinepage]; 14699 } else { 14700 $midpos = 0; 14701 } 14702 if ($midpos > 0) { 14703 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos)); 14704 $pend = substr($this->getPageBuffer($startlinepage), $midpos); 14705 } else { 14706 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos); 14707 $pend = ''; 14708 } 14709 if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl))))) OR ($yshift < 0)) { 14710 // calculate shifting amount 14711 $tw = $w; 14712 if (($plalign == 'J') AND $this->isRTLTextDir() AND ($this->num_columns > 1)) { 14713 $tw += $this->cMargin; 14714 } 14715 if ($this->lMargin != $prevlMargin) { 14716 $tw += ($prevlMargin - $this->lMargin); 14717 } 14718 if ($this->rMargin != $prevrMargin) { 14719 $tw += ($prevrMargin - $this->rMargin); 14720 } 14721 $one_space_width = $this->GetStringWidth(chr(32)); 14722 $mdiff = abs($tw - $linew); 14723 if ($plalign == 'C') { 14724 if ($this->rtl) { 14725 $t_x = -($mdiff / 2); 14726 } else { 14727 $t_x = ($mdiff / 2); 14728 } 14729 } elseif (($plalign == 'R') AND (!$this->rtl)) { 14730 // right alignment on LTR document 14731 if (intval($this->revstrpos($pmid, ')]')) == (intval($this->revstrpos($pmid, ' )]')) + 1)) { 14732 // remove last space (if any) 14733 $linew -= $one_space_width; 14734 $mdiff = abs($tw - $linew); 14735 } 14736 $t_x = $mdiff; 14737 } elseif (($plalign == 'L') AND ($this->rtl)) { 14738 // left alignment on RTL document 14739 if (($this->revstrpos($pmid, '[(') > 0) AND ((intval($this->revstrpos($pmid, '[( ')) == intval($this->revstrpos($pmid, '[('))) OR (intval($this->revstrpos($pmid, '[('.chr(0).chr(32))) == intval($this->revstrpos($pmid, '[('))))) { 14740 // remove first space (if any) 14741 $linew -= $one_space_width; 14742 } 14743 if ((strpos($pmid, '[(') > 0) AND (intval(strpos($pmid, '[(')) == (intval($this->revstrpos($pmid, '[('))))) { 14744 // remove last space (if any) 14745 $linew -= $one_space_width; 14746 if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) { 14747 $linew -= $one_space_width; 14748 } 14749 } 14750 $mdiff = abs($tw - $linew); 14751 $t_x = -$mdiff; 14752 } elseif (($plalign == 'J') AND ($plalign == $lalign)) { 14753 // Justification 14754 if ($this->isRTLTextDir()) { 14755 $t_x = $this->lMargin - $this->endlinex + $this->cMargin; 14756 } 14757 $no = 0; // spaces without trim 14758 $ns = 0; // spaces with trim 14759 $pmidtemp = $pmid; 14760 // escape special characters 14761 $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp); 14762 $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp); 14763 // search spaces 14764 if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER)) { 14765 $spacestr = $this->getSpaceString(); 14766 $maxkk = count($lnstring[1]) - 1; 14767 for ($kk=0; $kk <= $maxkk; ++$kk) { 14768 // restore special characters 14769 $lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]); 14770 $lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]); 14771 if ($kk == $maxkk) { 14772 if ($this->isRTLTextDir()) { 14773 $tvalue = ltrim($lnstring[1][$kk]); 14774 } else { 14775 $tvalue = rtrim($lnstring[1][$kk]); 14776 } 14777 } else { 14778 $tvalue = $lnstring[1][$kk]; 14779 } 14780 // store number of spaces on the strings 14781 $lnstring[2][$kk] = substr_count($lnstring[1][$kk], $spacestr); 14782 $lnstring[3][$kk] = substr_count($tvalue, $spacestr); 14783 // count total spaces on line 14784 $no += $lnstring[2][$kk]; 14785 $ns += $lnstring[3][$kk]; 14786 $lnstring[4][$kk] = $no; 14787 $lnstring[5][$kk] = $ns; 14788 } 14789 if ($this->isRTLTextDir()) { 14790 $t_x = $this->lMargin - $this->endlinex + $this->cMargin - (($no - $ns) * $one_space_width); 14791 } 14792 // calculate additional space to add to each space 14793 $spacelen = $one_space_width; 14794 $spacewidth = ((($tw - $linew) + (($no - $ns) * $spacelen)) / ($ns?$ns:1)) * $this->k; 14795 $spacewidthu = -1000 * (($tw - $linew) + ($no * $spacelen)) / ($ns?$ns:1) / $this->FontSize; 14796 $nsmax = $ns; 14797 $ns = 0; 14798 reset($lnstring); 14799 $offset = 0; 14800 $strcount = 0; 14801 $prev_epsposbeg = 0; 14802 $textpos = 0; 14803 if ($this->isRTLTextDir()) { 14804 $textpos = $this->wPt; 14805 } 14806 global $spacew; 14807 while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE, $offset) == 1) { 14808 // check if we are inside a string section '[( ... )]' 14809 $stroffset = strpos($pmid, '[(', $offset); 14810 if (($stroffset !== false) AND ($stroffset <= $strpiece[2][1])) { 14811 // set offset to the end of string section 14812 $offset = strpos($pmid, ')]', $stroffset); 14813 while (($offset !== false) AND ($pmid{($offset - 1)} == '\\')) { 14814 $offset = strpos($pmid, ')]', ($offset + 1)); 14815 } 14816 if ($offset === false) { 14817 $this->Error('HTML Justification: malformed PDF code.'); 14818 } 14819 continue; 14820 } 14821 if ($this->isRTLTextDir()) { 14822 $spacew = ($spacewidth * ($nsmax - $ns)); 14823 } else { 14824 $spacew = ($spacewidth * $ns); 14825 } 14826 $offset = $strpiece[2][1] + strlen($strpiece[2][0]); 14827 $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, $offset); 14828 $epsposend = strpos($pmid, $this->epsmarker.'Q', $offset) + strlen($this->epsmarker.'Q'); 14829 if ((($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend)) 14830 OR (($epsposbeg === false) AND ($epsposend > 0) AND ($offset < $epsposend))) { 14831 // shift EPS images 14832 $trx = sprintf('1 0 0 1 %.3F 0 cm', $spacew); 14833 $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, ($prev_epsposbeg - 6)); 14834 $pmid_b = substr($pmid, 0, $epsposbeg); 14835 $pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg)); 14836 $pmid_e = substr($pmid, $epsposend); 14837 $pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e; 14838 $offset = $epsposend; 14839 continue; 14840 14841 } 14842 $prev_epsposbeg = $epsposbeg; 14843 $currentxpos = 0; 14844 // shift blocks of code 14845 switch ($strpiece[2][0]) { 14846 case 'Td': 14847 case 'cm': 14848 case 'm': 14849 case 'l': { 14850 // get current X position 14851 preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches); 14852 $currentxpos = $xmatches[1]; 14853 $textpos = $currentxpos; 14854 if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) { 14855 if ($strcount == $maxkk) { 14856 if ($this->isRTLTextDir()) { 14857 $tvalue = $lnstring[1][$strcount]; 14858 } else { 14859 $tvalue = rtrim($lnstring[1][$strcount]); 14860 } 14861 } else { 14862 $tvalue = $lnstring[1][$strcount]; 14863 } 14864 $ns += substr_count($tvalue, $spacestr); 14865 ++$strcount; 14866 } 14867 if ($this->isRTLTextDir()) { 14868 $spacew = ($spacewidth * ($nsmax - $ns)); 14869 } 14870 // justify block 14871 $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', 14872 create_function('$matches', 'global $spacew; 14873 $newx = sprintf("%.2F",(floatval($matches[1]) + $spacew)); 14874 return "".$newx." ".$matches[2]." x*#!#*x".$matches[3].$matches[4];'), $pmid, 1); 14875 break; 14876 } 14877 case 're': { 14878 // justify block 14879 preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $xmatches); 14880 $currentxpos = $xmatches[1]; 14881 global $x_diff, $w_diff; 14882 $x_diff = 0; 14883 $w_diff = 0; 14884 if ($this->isRTLTextDir()) { // RTL 14885 if ($currentxpos < $textpos) { 14886 $x_diff = ($spacewidth * ($nsmax - $lnstring[5][$strcount])); 14887 $w_diff = ($spacewidth * $lnstring[3][$strcount]); 14888 } else { 14889 if ($strcount > 0) { 14890 $x_diff = ($spacewidth * ($nsmax - $lnstring[5][($strcount - 1)])); 14891 $w_diff = ($spacewidth * $lnstring[3][($strcount - 1)]); 14892 } 14893 } 14894 } else { // LTR 14895 if ($currentxpos > $textpos) { 14896 if ($strcount > 0) { 14897 $x_diff = ($spacewidth * $lnstring[4][($strcount - 1)]); 14898 } 14899 $w_diff = ($spacewidth * $lnstring[3][$strcount]); 14900 } else { 14901 if ($strcount > 1) { 14902 $x_diff = ($spacewidth * $lnstring[4][($strcount - 2)]); 14903 } 14904 if ($strcount > 0) { 14905 $w_diff = ($spacewidth * $lnstring[3][($strcount - 1)]); 14906 } 14907 } 14908 } 14909 $pmid = preg_replace_callback('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', 14910 create_function('$matches', 'global $x_diff, $w_diff; 14911 $newx = sprintf("%.2F",(floatval($matches[1]) + $x_diff)); 14912 $neww = sprintf("%.2F",(floatval($matches[3]) + $w_diff)); 14913 return "".$newx." ".$matches[2]." ".$neww." ".$matches[4]." x*#!#*x".$matches[5].$matches[6];'), $pmid, 1); 14914 break; 14915 } 14916 case 'c': { 14917 // get current X position 14918 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); 14919 $currentxpos = $xmatches[1]; 14920 // justify block 14921 $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', 14922 create_function('$matches', 'global $spacew; 14923 $newx1 = sprintf("%.3F",(floatval($matches[1]) + $spacew)); 14924 $newx2 = sprintf("%.3F",(floatval($matches[3]) + $spacew)); 14925 $newx3 = sprintf("%.3F",(floatval($matches[5]) + $spacew)); 14926 return "".$newx1." ".$matches[2]." ".$newx2." ".$matches[4]." ".$newx3." ".$matches[6]." x*#!#*x".$matches[7].$matches[8];'), $pmid, 1); 14927 break; 14928 } 14929 } 14930 // shift the annotations and links 14931 if (isset($this->PageAnnots[$this->page])) { 14932 $cxpos = ($currentxpos / $this->k); 14933 $lmpos = ($this->lMargin + $this->cMargin + $this->feps); 14934 foreach ($this->PageAnnots[$this->page] as $pak => $pac) { 14935 if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) { 14936 if ($cxpos > $lmpos) { 14937 $this->PageAnnots[$this->page][$pak]['x'] += (($spacew - $one_space_width) / $this->k); 14938 $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k); 14939 } else { 14940 $this->PageAnnots[$this->page][$pak]['w'] += ((($spacewidth * $pac['numspaces']) - $one_space_width) / $this->k); 14941 } 14942 break; 14943 } 14944 } 14945 } 14946 } // end of while 14947 // remove markers 14948 $pmid = str_replace('x*#!#*x', '', $pmid); 14949 if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) { 14950 // multibyte characters 14951 $spacew = $spacewidthu; 14952 $pmidtemp = $pmid; 14953 // escape special characters 14954 $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp); 14955 $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp); 14956 $pmid = preg_replace_callback("/\[\(([^\)]*)\)\]/x", 14957 create_function('$matches', 'global $spacew; 14958 $matches[1] = str_replace("#!#OP#!#", "(", $matches[1]); 14959 $matches[1] = str_replace("#!#CP#!#", ")", $matches[1]); 14960 return "[(".str_replace(chr(0).chr(32), ") ".sprintf("%.3F", $spacew)." (", $matches[1]).")]";'), $pmidtemp); 14961 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend); 14962 $endlinepos = strlen($pstart."\n".$pmid."\n"); 14963 } else { 14964 // non-unicode (single-byte characters) 14965 $rs = sprintf('%.3F Tw', $spacewidth); 14966 $pmid = preg_replace("/\[\(/x", $rs.' [(', $pmid); 14967 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend); 14968 $endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n"); 14969 } 14970 } 14971 } // end of J 14972 } // end if $startlinex 14973 if (($t_x != 0) OR ($yshift < 0)) { 14974 // shift the line 14975 $trx = sprintf('1 0 0 1 %.3F %.3F cm', ($t_x * $this->k), ($yshift * $this->k)); 14976 $this->setPageBuffer($startlinepage, $pstart."\nq\n".$trx."\n".$pmid."\nQ\n".$pend); 14977 $endlinepos = strlen($pstart."\nq\n".$trx."\n".$pmid."\nQ\n"); 14978 // shift the annotations and links 14979 if (isset($this->PageAnnots[$this->page])) { 14980 foreach ($this->PageAnnots[$this->page] as $pak => $pac) { 14981 if ($pak >= $pask) { 14982 $this->PageAnnots[$this->page][$pak]['x'] += $t_x; 14983 $this->PageAnnots[$this->page][$pak]['y'] -= $yshift; 14984 } 14985 } 14986 } 14987 $this->y -= $yshift; 14988 } 14989 } 14990 $this->newline = false; 14991 $pbrk = $this->checkPageBreak($this->lasth); 14992 $startlinex = $this->x; 14993 $startliney = $this->y; 14994 $minstartliney = $startliney; 14995 $maxbottomliney = ($this->y + (($fontsize * $this->cell_height_ratio) / $this->k)); 14996 $startlinepage = $this->page; 14997 if (isset($endlinepos) AND (!$pbrk)) { 14998 $startlinepos = $endlinepos; 14999 } else { 15000 if (!$this->InFooter) { 15001 if (isset($this->footerlen[$this->page])) { 15002 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page]; 15003 } else { 15004 $this->footerpos[$this->page] = $this->pagelen[$this->page]; 15005 } 15006 $startlinepos = $this->footerpos[$this->page]; 15007 } else { 15008 $startlinepos = $this->pagelen[$this->page]; 15009 } 15010 } 15011 unset($endlinepos); 15012 $plalign = $lalign; 15013 if (isset($this->PageAnnots[$this->page])) { 15014 $pask = count($this->PageAnnots[$this->page]); 15015 } else { 15016 $pask = 0; 15017 } 15018 $this->SetFont($fontname, $fontstyle, $fontsize); 15019 if ($wfill) { 15020 $this->SetFillColorArray($this->bgcolor); 15021 } 15022 } // end newline 15023 if (isset($opentagpos)) { 15024 unset($opentagpos); 15025 } 15026 if ($dom[$key]['tag']) { 15027 if ($dom[$key]['opening']) { 15028 // get text indentation (if any) 15029 if (isset($dom[$key]['text-indent']) AND $dom[$key]['block']) { 15030 $this->textindent = $dom[$key]['text-indent']; 15031 $this->newline = true; 15032 } 15033 if ($dom[$key]['value'] == 'table') { 15034 // available page width 15035 if ($this->rtl) { 15036 $wtmp = $this->x - $this->lMargin; 15037 } else { 15038 $wtmp = $this->w - $this->rMargin - $this->x; 15039 } 15040 if (isset($dom[$key]['attribute']['nested']) AND ($dom[$key]['attribute']['nested'] == 'true')) { 15041 // add margin for nested tables 15042 $wtmp -= $this->cMargin; 15043 } 15044 // table width 15045 if (isset($dom[$key]['width'])) { 15046 $table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px'); 15047 } else { 15048 $table_width = $wtmp; 15049 } 15050 } 15051 if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) { 15052 $trid = $dom[$key]['parent']; 15053 $table_el = $dom[$trid]['parent']; 15054 if (!isset($dom[$table_el]['cols'])) { 15055 $dom[$table_el]['cols'] = $dom[$trid]['cols']; 15056 } 15057 $oldmargin = $this->cMargin; 15058 if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) { 15059 $currentcmargin = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px'); 15060 } else { 15061 $currentcmargin = 0; 15062 } 15063 $this->cMargin = $currentcmargin; 15064 if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellspacing'])) { 15065 $cellspacing = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellspacing'], 1, 'px'); 15066 } else { 15067 $cellspacing = 0; 15068 } 15069 if ($this->rtl) { 15070 $cellspacingx = -$cellspacing; 15071 } else { 15072 $cellspacingx = $cellspacing; 15073 } 15074 $colspan = $dom[$key]['attribute']['colspan']; 15075 $table_columns_width = ($table_width - ($cellspacing * ($dom[$table_el]['cols'] - 1))); 15076 $wtmp = ($colspan * ($table_columns_width / $dom[$table_el]['cols'])) + (($colspan - 1) * $cellspacing); 15077 if (isset($dom[$key]['width'])) { 15078 $cellw = $this->getHTMLUnitToUnits($dom[$key]['width'], $table_columns_width, 'px'); 15079 } else { 15080 $cellw = $wtmp; 15081 } 15082 if (isset($dom[$key]['height'])) { 15083 // minimum cell height 15084 $cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px'); 15085 } else { 15086 $cellh = 0; 15087 } 15088 if (isset($dom[$key]['content'])) { 15089 $cell_content = $dom[$key]['content']; 15090 } else { 15091 $cell_content = ' '; 15092 } 15093 $tagtype = $dom[$key]['value']; 15094 $parentid = $key; 15095 while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) { 15096 // move $key index forward 15097 ++$key; 15098 } 15099 if (!isset($dom[$trid]['startpage'])) { 15100 $dom[$trid]['startpage'] = $this->page; 15101 } else { 15102 $this->setPage($dom[$trid]['startpage']); 15103 } 15104 if (!isset($dom[$trid]['starty'])) { 15105 $dom[$trid]['starty'] = $this->y; 15106 } else { 15107 $this->y = $dom[$trid]['starty']; 15108 } 15109 if (!isset($dom[$trid]['startx'])) { 15110 $dom[$trid]['startx'] = $this->x; 15111 } else { 15112 $this->x += ($cellspacingx / 2); 15113 } 15114 if (isset($dom[$parentid]['attribute']['rowspan'])) { 15115 $rowspan = intval($dom[$parentid]['attribute']['rowspan']); 15116 } else { 15117 $rowspan = 1; 15118 } 15119 // skip row-spanned cells started on the previous rows 15120 if (isset($dom[$table_el]['rowspans'])) { 15121 $rsk = 0; 15122 $rskmax = count($dom[$table_el]['rowspans']); 15123 while ($rsk < $rskmax) { 15124 $trwsp = $dom[$table_el]['rowspans'][$rsk]; 15125 $rsstartx = $trwsp['startx']; 15126 $rsendx = $trwsp['endx']; 15127 // account for margin changes 15128 if ($trwsp['startpage'] < $this->page) { 15129 if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$trwsp['startpage']]['orm'])) { 15130 $dl = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$trwsp['startpage']]['orm']); 15131 $rsstartx -= $dl; 15132 $rsendx -= $dl; 15133 } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$trwsp['startpage']]['olm'])) { 15134 $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$trwsp['startpage']]['olm']); 15135 $rsstartx += $dl; 15136 $rsendx += $dl; 15137 } 15138 } 15139 if (($trwsp['rowspan'] > 0) 15140 AND ($rsstartx > ($this->x - $cellspacing - $currentcmargin - $this->feps)) 15141 AND ($rsstartx < ($this->x + $cellspacing + $currentcmargin + $this->feps)) 15142 AND (($trwsp['starty'] < ($this->y - $this->feps)) OR ($trwsp['startpage'] < $this->page))) { 15143 // set the starting X position of the current cell 15144 $this->x = $rsendx + $cellspacingx; 15145 if (($trwsp['rowspan'] == 1) 15146 AND (isset($dom[$trid]['endy'])) 15147 AND (isset($dom[$trid]['endpage'])) 15148 AND ($trwsp['endpage'] == $dom[$trid]['endpage'])) { 15149 // set ending Y position for row 15150 $dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']); 15151 $dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy']; 15152 } 15153 $rsk = 0; 15154 } else { 15155 ++$rsk; 15156 } 15157 } 15158 } 15159 // add rowspan information to table element 15160 if ($rowspan > 1) { 15161 $trsid = array_push($dom[$table_el]['rowspans'], array('trid' => $trid, 'rowspan' => $rowspan, 'mrowspan' => $rowspan, 'colspan' => $colspan, 'startpage' => $this->page, 'startx' => $this->x, 'starty' => $this->y)); 15162 } 15163 $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x)); 15164 if ($rowspan > 1) { 15165 $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1); 15166 } 15167 // push background colors 15168 if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) { 15169 $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor']; 15170 } 15171 $prevLastH = $this->lasth; 15172 // ****** write the cell content ****** 15173 $this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true); 15174 $this->lasth = $prevLastH; 15175 $this->cMargin = $oldmargin; 15176 $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x; 15177 // update the end of row position 15178 if ($rowspan <= 1) { 15179 if (isset($dom[$trid]['endy'])) { 15180 if ($this->page == $dom[$trid]['endpage']) { 15181 $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']); 15182 } elseif ($this->page > $dom[$trid]['endpage']) { 15183 $dom[$trid]['endy'] = $this->y; 15184 } 15185 } else { 15186 $dom[$trid]['endy'] = $this->y; 15187 } 15188 if (isset($dom[$trid]['endpage'])) { 15189 $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']); 15190 } else { 15191 $dom[$trid]['endpage'] = $this->page; 15192 } 15193 } else { 15194 // account for row-spanned cells 15195 $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x; 15196 $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y; 15197 $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page; 15198 } 15199 if (isset($dom[$table_el]['rowspans'])) { 15200 // update endy and endpage on rowspanned cells 15201 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) { 15202 if ($trwsp['rowspan'] > 0) { 15203 if (isset($dom[$trid]['endpage'])) { 15204 if ($trwsp['endpage'] == $dom[$trid]['endpage']) { 15205 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']); 15206 } elseif ($trwsp['endpage'] < $dom[$trid]['endpage']) { 15207 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy']; 15208 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage']; 15209 } else { 15210 $dom[$trid]['endy'] = $this->pagedim[$dom[$trid]['endpage']]['hk'] - $this->pagedim[$dom[$trid]['endpage']]['bm']; 15211 } 15212 } 15213 } 15214 } 15215 } 15216 $this->x += ($cellspacingx / 2); 15217 } else { 15218 // opening tag (or self-closing tag) 15219 if (!isset($opentagpos)) { 15220 if (!$this->InFooter) { 15221 if (isset($this->footerlen[$this->page])) { 15222 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page]; 15223 } else { 15224 $this->footerpos[$this->page] = $this->pagelen[$this->page]; 15225 } 15226 $opentagpos = $this->footerpos[$this->page]; 15227 } 15228 } 15229 $this->openHTMLTagHandler($dom, $key, $cell); 15230 } 15231 } else { 15232 // closing tag 15233 $this->closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney); 15234 } 15235 } elseif (strlen($dom[$key]['value']) > 0) { 15236 // print list-item 15237 if (!$this->empty_string($this->lispacer)) { 15238 $this->SetFont($pfontname, $pfontstyle, $pfontsize); 15239 $this->lasth = $this->FontSize * $this->cell_height_ratio; 15240 $minstartliney = $this->y; 15241 $maxbottomliney = ($startliney + ($this->FontSize * $this->cell_height_ratio)); 15242 $this->putHtmlListBullet($this->listnum, $this->lispacer, $pfontsize); 15243 $this->SetFont($curfontname, $curfontstyle, $curfontsize); 15244 $this->lasth = $this->FontSize * $this->cell_height_ratio; 15245 if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) { 15246 $pfontascent = $this->getFontAscent($pfontname, $pfontstyle, $pfontsize); 15247 $pfontdescent = $this->getFontDescent($pfontname, $pfontstyle, $pfontsize); 15248 $this->y += ((($pfontsize - $curfontsize) * $this->cell_height_ratio / $this->k) + $pfontascent - $curfontascent - $pfontdescent + $curfontdescent) / 2; 15249 $minstartliney = min($this->y, $minstartliney); 15250 $maxbottomliney = max(($this->y + (($pfontsize * $this->cell_height_ratio) / $this->k)), $maxbottomliney); 15251 } 15252 } 15253 // text 15254 $this->htmlvspace = 0; 15255 if ((!$this->premode) AND $this->isRTLTextDir()) { 15256 // reverse spaces order 15257 $len1 = strlen($dom[$key]['value']); 15258 $lsp = $len1 - strlen(ltrim($dom[$key]['value'])); 15259 $rsp = $len1 - strlen(rtrim($dom[$key]['value'])); 15260 $tmpstr = ''; 15261 if ($rsp > 0) { 15262 $tmpstr .= substr($dom[$key]['value'], -$rsp); 15263 } 15264 $tmpstr .= trim($dom[$key]['value']); 15265 if ($lsp > 0) { 15266 $tmpstr .= substr($dom[$key]['value'], 0, $lsp); 15267 } 15268 $dom[$key]['value'] = $tmpstr; 15269 } 15270 if ($newline) { 15271 if (!$this->premode) { 15272 $prelen = strlen($dom[$key]['value']); 15273 if ($this->isRTLTextDir()) { 15274 $dom[$key]['value'] = rtrim($dom[$key]['value']).chr(0); 15275 } else { 15276 $dom[$key]['value'] = ltrim($dom[$key]['value']); 15277 } 15278 $postlen = strlen($dom[$key]['value']); 15279 if (($postlen == 0) AND ($prelen > 0)) { 15280 $dom[$key]['trimmed_space'] = true; 15281 } 15282 } 15283 $newline = false; 15284 $firstblock = true; 15285 } else { 15286 $firstblock = false; 15287 } 15288 $strrest = ''; 15289 if ($this->rtl) { 15290 $this->x -= $this->textindent; 15291 } else { 15292 $this->x += $this->textindent; 15293 } 15294 if (!empty($this->HREF) AND (isset($this->HREF['url']))) { 15295 // HTML <a> Link 15296 $strrest = $this->addHtmlLink($this->HREF['url'], $dom[$key]['value'], $wfill, true, $this->HREF['color'], $this->HREF['style'], true); 15297 } else { 15298 // ****** write only until the end of the line and get the rest ****** 15299 $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock, 0); 15300 } 15301 $this->textindent = 0; 15302 15303 if (strlen($strrest) > 0) { 15304 // store the remaining string on the previous $key position 15305 $this->newline = true; 15306 if ($cell) { 15307 if ($this->rtl) { 15308 $this->x -= $this->cMargin; 15309 } else { 15310 $this->x += $this->cMargin; 15311 } 15312 } 15313 if ($strrest == $dom[$key]['value']) { 15314 // used to avoid infinite loop 15315 ++$loop; 15316 } else { 15317 $loop = 0; 15318 } 15319 if (!empty($this->HREF) AND (isset($this->HREF['url']))) { 15320 $dom[$key]['value'] = trim($strrest); 15321 } elseif ($this->premode) { 15322 $dom[$key]['value'] = $strrest; 15323 } elseif ($this->isRTLTextDir()) { 15324 $dom[$key]['value'] = rtrim($strrest); 15325 } else { 15326 $dom[$key]['value'] = ltrim($strrest); 15327 } 15328 if ($loop < 3) { 15329 --$key; 15330 } 15331 } else { 15332 $loop = 0; 15333 } 15334 } 15335 ++$key; 15336 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')) { 15337 if ( (!$undo) AND (($this->start_transaction_page == ($this->numpages - 1)) OR ($this->y < $this->start_transaction_y))) { 15338 // restore previous object 15339 $this->rollbackTransaction(true); 15340 // restore previous values 15341 foreach ($this_method_vars as $vkey => $vval) { 15342 $$vkey = $vval; 15343 } 15344 // add a page (or trig AcceptPageBreak() for multicolumn mode) 15345 $pre_y = $this->y; 15346 if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) { 15347 $startliney = $this->y; 15348 } 15349 $undo = true; // avoid infinite loop 15350 } else { 15351 $undo = false; 15352 } 15353 } 15354 } // end for each $key 15355 // align the last line 15356 if (isset($startlinex)) { 15357 $yshift = $minstartliney - $startliney; 15358 if (($yshift > 0) OR ($this->page > $startlinepage)) { 15359 $yshift = 0; 15360 } 15361 $t_x = 0; 15362 // the last line must be shifted to be aligned as requested 15363 $linew = abs($this->endlinex - $startlinex); 15364 $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos); 15365 if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) { 15366 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 15367 $midpos = min($opentagpos, $this->footerpos[$startlinepage]); 15368 } elseif (isset($opentagpos)) { 15369 $midpos = $opentagpos; 15370 } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) { 15371 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 15372 $midpos = $this->footerpos[$startlinepage]; 15373 } else { 15374 $midpos = 0; 15375 } 15376 if ($midpos > 0) { 15377 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos)); 15378 $pend = substr($this->getPageBuffer($startlinepage), $midpos); 15379 } else { 15380 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos); 15381 $pend = ''; 15382 } 15383 if ((isset($plalign) AND ((($plalign == 'C') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl))))) OR ($yshift < 0)) { 15384 // calculate shifting amount 15385 $tw = $w; 15386 if ($this->lMargin != $prevlMargin) { 15387 $tw += ($prevlMargin - $this->lMargin); 15388 } 15389 if ($this->rMargin != $prevrMargin) { 15390 $tw += ($prevrMargin - $this->rMargin); 15391 } 15392 $one_space_width = $this->GetStringWidth(chr(32)); 15393 $mdiff = abs($tw - $linew); 15394 if ($plalign == 'C') { 15395 if ($this->rtl) { 15396 $t_x = -($mdiff / 2); 15397 } else { 15398 $t_x = ($mdiff / 2); 15399 } 15400 } elseif (($plalign == 'R') AND (!$this->rtl)) { 15401 // right alignment on LTR document 15402 if (intval($this->revstrpos($pmid, ')]')) == (intval($this->revstrpos($pmid, ' )]')) + 1)) { 15403 // remove last space (if any) 15404 $linew -= $one_space_width; 15405 $mdiff = abs($tw - $linew); 15406 } 15407 $t_x = $mdiff; 15408 } elseif (($plalign == 'L') AND ($this->rtl)) { 15409 // left alignment on RTL document 15410 if (($this->revstrpos($pmid, '[(') > 0) AND ((intval($this->revstrpos($pmid, '[( ')) == intval($this->revstrpos($pmid, '[('))) OR (intval($this->revstrpos($pmid, '[('.chr(0).chr(32))) == intval($this->revstrpos($pmid, '[('))))) { 15411 // remove first space (if any) 15412 $linew -= $one_space_width; 15413 } 15414 if ((strpos($pmid, '[(') > 0) AND (intval(strpos($pmid, '[(')) == (intval($this->revstrpos($pmid, '[('))))) { 15415 // remove last space (if any) 15416 $linew -= $one_space_width; 15417 if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) { 15418 $linew -= $one_space_width; 15419 } 15420 } 15421 $mdiff = abs($tw - $linew); 15422 $t_x = -$mdiff; 15423 } 15424 } // end if startlinex 15425 if (($t_x != 0) OR ($yshift < 0)) { 15426 // shift the line 15427 $trx = sprintf('1 0 0 1 %.3F %.3F cm', ($t_x * $this->k), ($yshift * $this->k)); 15428 $this->setPageBuffer($startlinepage, $pstart."\nq\n".$trx."\n".$pmid."\nQ\n".$pend); 15429 $endlinepos = strlen($pstart."\nq\n".$trx."\n".$pmid."\nQ\n"); 15430 // shift the annotations and links 15431 if (isset($this->PageAnnots[$this->page])) { 15432 foreach ($this->PageAnnots[$this->page] as $pak => $pac) { 15433 if ($pak >= $pask) { 15434 $this->PageAnnots[$this->page][$pak]['x'] += $t_x; 15435 $this->PageAnnots[$this->page][$pak]['y'] -= $yshift; 15436 } 15437 } 15438 } 15439 $this->y -= $yshift; 15440 } 15441 } 15442 if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) { 15443 $this->Ln($this->lasth); 15444 if ($this->y < $maxbottomliney) { 15445 $this->y = $maxbottomliney; 15446 } 15447 } 15448 // restore previous values 15449 $this->setGraphicVars($gvars); 15450 if ($this->page > $prevPage) { 15451 $this->lMargin = $this->pagedim[$this->page]['olm']; 15452 $this->rMargin = $this->pagedim[$this->page]['orm']; 15453 } 15454 // restore previous list state 15455 $this->cell_height_ratio = $prev_cell_height_ratio; 15456 $this->listnum = $prev_listnum; 15457 $this->listordered = $prev_listordered; 15458 $this->listcount = $prev_listcount; 15459 $this->lispacer = $prev_lispacer; 15460 unset($dom); 15461 } 15462 15463 /** 15464 * Process opening tags. 15465 * @param array $dom html dom array 15466 * @param int $key current element id 15467 * @param boolean $cell if true add the default cMargin space to each new line (default false). 15468 * @access protected 15469 */ 15470 protected function openHTMLTagHandler(&$dom, $key, $cell) { 15471 $tag = $dom[$key]; 15472 $parent = $dom[($dom[$key]['parent'])]; 15473 $firstorlast = ($key == 1); 15474 // check for text direction attribute 15475 if (isset($tag['attribute']['dir'])) { 15476 $this->setTempRTL($tag['attribute']['dir']); 15477 } else { 15478 $this->tmprtl = false; 15479 } 15480 if ($tag['block']) { 15481 $hbz = 0; // distance from y to line bottom 15482 $hb = 0; // vertical space between block tags 15483 // calculate vertical space for block tags 15484 if (isset($this->tagvspaces[$tag['value']][0]['h']) AND ($this->tagvspaces[$tag['value']][0]['h'] >= 0)) { 15485 $cur_h = $this->tagvspaces[$tag['value']][0]['h']; 15486 } elseif (isset($tag['fontsize'])) { 15487 $cur_h = ($tag['fontsize'] / $this->k) * $this->cell_height_ratio; 15488 } else { 15489 $cur_h = $this->FontSize * $this->cell_height_ratio; 15490 } 15491 if (isset($this->tagvspaces[$tag['value']][0]['n'])) { 15492 $n = $this->tagvspaces[$tag['value']][0]['n']; 15493 } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) { 15494 $n = 0.6; 15495 } else { 15496 $n = 1; 15497 } 15498 $hb = ($n * $cur_h); 15499 if (($this->htmlvspace <= 0) AND ($n > 0)) { 15500 if (isset($parent['fontsize'])) { 15501 $hbz = (($parent['fontsize'] / $this->k) * $this->cell_height_ratio); 15502 } else { 15503 $hbz = $this->FontSize * $this->cell_height_ratio; 15504 } 15505 } 15506 } 15507 //Opening tag 15508 switch($tag['value']) { 15509 case 'table': { 15510 $cp = 0; 15511 $cs = 0; 15512 $dom[$key]['rowspans'] = array(); 15513 if (!isset($dom[$key]['attribute']['nested']) OR ($dom[$key]['attribute']['nested'] != 'true')) { 15514 // set table header 15515 if (!$this->empty_string($dom[$key]['thead'])) { 15516 // set table header 15517 $this->thead = $dom[$key]['thead']; 15518 if (!isset($this->theadMargins) OR (empty($this->theadMargins))) { 15519 $this->theadMargins = array(); 15520 $this->theadMargins['cmargin'] = $this->cMargin; 15521 } 15522 } 15523 } 15524 if (isset($tag['attribute']['cellpadding'])) { 15525 $cp = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px'); 15526 $this->oldcMargin = $this->cMargin; 15527 $this->cMargin = $cp; 15528 } 15529 if (isset($tag['attribute']['cellspacing'])) { 15530 $cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px'); 15531 } 15532 if ($this->checkPageBreak(((2 * $cp) + (2 * $cs) + $this->lasth), '', false)) { 15533 $this->inthead = true; 15534 // add a page (or trig AcceptPageBreak() for multicolumn mode) 15535 $this->checkPageBreak($this->PageBreakTrigger + 1); 15536 } 15537 break; 15538 } 15539 case 'tr': { 15540 // array of columns positions 15541 $dom[$key]['cellpos'] = array(); 15542 break; 15543 } 15544 case 'hr': { 15545 $wtmp = $this->w - $this->lMargin - $this->rMargin; 15546 if ((isset($tag['attribute']['width'])) AND ($tag['attribute']['width'] != '')) { 15547 $hrWidth = $this->getHTMLUnitToUnits($tag['attribute']['width'], $wtmp, 'px'); 15548 } else { 15549 $hrWidth = $wtmp; 15550 } 15551 if ((isset($tag['height'])) AND ($tag['height'] != '')) { 15552 $hrHeight = $this->getHTMLUnitToUnits($tag['height'], 1, 'px'); 15553 } else { 15554 $hrHeight = $this->GetLineWidth(); 15555 } 15556 $this->addHTMLVertSpace($hbz, ($hrHeight / 2), $cell, $firstorlast); 15557 $x = $this->GetX(); 15558 $y = $this->GetY(); 15559 $prevlinewidth = $this->GetLineWidth(); 15560 $this->SetLineWidth($hrHeight); 15561 $this->Line($x, $y, $x + $hrWidth, $y); 15562 $this->SetLineWidth($prevlinewidth); 15563 $this->addHTMLVertSpace(($hrHeight / 2), 0, $cell, !isset($dom[($key + 1)])); 15564 break; 15565 } 15566 case 'a': { 15567 if (array_key_exists('href', $tag['attribute'])) { 15568 $this->HREF['url'] = $tag['attribute']['href']; 15569 } 15570 $this->HREF['color'] = $this->htmlLinkColorArray; 15571 $this->HREF['style'] = $this->htmlLinkFontStyle; 15572 if (array_key_exists('style', $tag['attribute'])) { 15573 // get style attributes 15574 preg_match_all('/([^;:\s]*):([^;]*)/', $tag['attribute']['style'], $style_array, PREG_PATTERN_ORDER); 15575 $astyle = array(); 15576 while (list($id, $name) = each($style_array[1])) { 15577 $name = strtolower($name); 15578 $astyle[$name] = trim($style_array[2][$id]); 15579 } 15580 if (isset($astyle['color'])) { 15581 $this->HREF['color'] = $this->convertHTMLColorToDec($astyle['color']); 15582 } 15583 if (isset($astyle['text-decoration'])) { 15584 $this->HREF['style'] = ''; 15585 $decors = explode(' ', strtolower($astyle['text-decoration'])); 15586 foreach ($decors as $dec) { 15587 $dec = trim($dec); 15588 if (!$this->empty_string($dec)) { 15589 if ($dec{0} == 'u') { 15590 // underline 15591 $this->HREF['style'] .= 'U'; 15592 } elseif ($dec{0} == 'l') { 15593 // line-trough 15594 $this->HREF['style'] .= 'D'; 15595 } elseif ($dec{0} == 'o') { 15596 // overline 15597 $this->HREF['style'] .= 'O'; 15598 } 15599 } 15600 } 15601 } 15602 } 15603 break; 15604 } 15605 case 'img': { 15606 if (isset($tag['attribute']['src'])) { 15607 // replace relative path with real server path 15608 if (($tag['attribute']['src'][0] == '/') AND ($_SERVER['DOCUMENT_ROOT'] != '/')) { 15609 $findroot = strpos($tag['attribute']['src'], $_SERVER['DOCUMENT_ROOT']); 15610 if (($findroot === false) OR ($findroot > 1)) { 15611 $tag['attribute']['src'] = $_SERVER['DOCUMENT_ROOT'].$tag['attribute']['src']; 15612 } 15613 } 15614 $tag['attribute']['src'] = urldecode($tag['attribute']['src']); 15615 $type = $this->getImageFileType($tag['attribute']['src']); 15616 $testscrtype = @parse_url($tag['attribute']['src']); 15617 if (!isset($testscrtype['query']) OR empty($testscrtype['query'])) { 15618 // convert URL to server path 15619 $tag['attribute']['src'] = str_replace(K_PATH_URL, K_PATH_MAIN, $tag['attribute']['src']); 15620 } 15621 if (!isset($tag['attribute']['width'])) { 15622 $tag['attribute']['width'] = 0; 15623 } 15624 if (!isset($tag['attribute']['height'])) { 15625 $tag['attribute']['height'] = 0; 15626 } 15627 //if (!isset($tag['attribute']['align'])) { 15628 // the only alignment supported is "bottom" 15629 // further development is required for other modes. 15630 $tag['attribute']['align'] = 'bottom'; 15631 //} 15632 switch($tag['attribute']['align']) { 15633 case 'top': { 15634 $align = 'T'; 15635 break; 15636 } 15637 case 'middle': { 15638 $align = 'M'; 15639 break; 15640 } 15641 case 'bottom': { 15642 $align = 'B'; 15643 break; 15644 } 15645 default: { 15646 $align = 'B'; 15647 break; 15648 } 15649 } 15650 $prevy = $this->y; 15651 $xpos = $this->GetX(); 15652 // eliminate marker spaces 15653 if (isset($dom[($key - 1)])) { 15654 if (($dom[($key - 1)]['value'] == ' ') OR (isset($dom[($key - 1)]['trimmed_space']))) { 15655 $xpos -= $this->GetStringWidth(chr(32)); 15656 } elseif ($this->rtl AND $dom[($key - 1)]['value'] == ' ') { 15657 $xpos -= (2 * $this->GetStringWidth(chr(32))); 15658 } 15659 } 15660 $imglink = ''; 15661 if (isset($this->HREF['url']) AND !$this->empty_string($this->HREF['url'])) { 15662 $imglink = $this->HREF['url']; 15663 if ($imglink{0} == '#') { 15664 // convert url to internal link 15665 $page = intval(substr($imglink, 1)); 15666 $imglink = $this->AddLink(); 15667 $this->SetLink($imglink, 0, $page); 15668 } 15669 } 15670 $border = 0; 15671 if (isset($tag['attribute']['border']) AND !empty($tag['attribute']['border'])) { 15672 // currently only support 1 (frame) or a combination of 'LTRB' 15673 $border = $tag['attribute']['border']; 15674 } 15675 $iw = ''; 15676 if (isset($tag['attribute']['width'])) { 15677 $iw = $this->getHTMLUnitToUnits($tag['attribute']['width'], 1, 'px', false); 15678 } 15679 $ih = ''; 15680 if (isset($tag['attribute']['height'])) { 15681 $ih = $this->getHTMLUnitToUnits($tag['attribute']['height'], 1, 'px', false); 15682 } 15683 if (($type == 'eps') OR ($type == 'ai')) { 15684 $this->ImageEps($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, true, $align, '', $border, true); 15685 } elseif ($type == 'svg') { 15686 $this->ImageSVG($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, $align, '', $border, true); 15687 } else { 15688 $this->Image($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border, false, false, true); 15689 } 15690 switch($align) { 15691 case 'T': { 15692 $this->y = $prevy; 15693 break; 15694 } 15695 case 'M': { 15696 $this->y = (($this->img_rb_y + $prevy - ($tag['fontsize'] / $this->k)) / 2) ; 15697 break; 15698 } 15699 case 'B': { 15700 $this->y = $this->img_rb_y - ($tag['fontsize'] / $this->k); 15701 break; 15702 } 15703 } 15704 } 15705 break; 15706 } 15707 case 'dl': { 15708 ++$this->listnum; 15709 if ($this->listnum == 1) { 15710 $this->addHTMLVertSpace($hbz, $hb, $cell, $firstorlast); 15711 } else { 15712 $this->addHTMLVertSpace(0, 0, $cell, $firstorlast); 15713 } 15714 break; 15715 } 15716 case 'dt': { 15717 $this->addHTMLVertSpace($hbz, 0, $cell, $firstorlast); 15718 break; 15719 } 15720 case 'dd': { 15721 if ($this->rtl) { 15722 $this->rMargin += $this->listindent; 15723 } else { 15724 $this->lMargin += $this->listindent; 15725 } 15726 ++$this->listindentlevel; 15727 $this->addHTMLVertSpace($hbz, 0, $cell, $firstorlast); 15728 break; 15729 } 15730 case 'ul': 15731 case 'ol': { 15732 ++$this->listnum; 15733 if ($tag['value'] == 'ol') { 15734 $this->listordered[$this->listnum] = true; 15735 } else { 15736 $this->listordered[$this->listnum] = false; 15737 } 15738 if (isset($tag['attribute']['start'])) { 15739 $this->listcount[$this->listnum] = intval($tag['attribute']['start']) - 1; 15740 } else { 15741 $this->listcount[$this->listnum] = 0; 15742 } 15743 if ($this->rtl) { 15744 $this->rMargin += $this->listindent; 15745 } else { 15746 $this->lMargin += $this->listindent; 15747 } 15748 ++$this->listindentlevel; 15749 if ($this->listnum == 1) { 15750 $this->addHTMLVertSpace($hbz, $hb, $cell, $firstorlast); 15751 } else { 15752 $this->addHTMLVertSpace(0, 0, $cell, $firstorlast); 15753 } 15754 break; 15755 } 15756 case 'li': { 15757 $this->addHTMLVertSpace($hbz, 0, $cell, $firstorlast); 15758 if ($this->listordered[$this->listnum]) { 15759 // ordered item 15760 if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) { 15761 $this->lispacer = $parent['attribute']['type']; 15762 } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) { 15763 $this->lispacer = $parent['listtype']; 15764 } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) { 15765 $this->lispacer = $this->lisymbol; 15766 } else { 15767 $this->lispacer = '#'; 15768 } 15769 ++$this->listcount[$this->listnum]; 15770 if (isset($tag['attribute']['value'])) { 15771 $this->listcount[$this->listnum] = intval($tag['attribute']['value']); 15772 } 15773 } else { 15774 // unordered item 15775 if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) { 15776 $this->lispacer = $parent['attribute']['type']; 15777 } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) { 15778 $this->lispacer = $parent['listtype']; 15779 } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) { 15780 $this->lispacer = $this->lisymbol; 15781 } else { 15782 $this->lispacer = '!'; 15783 } 15784 } 15785 break; 15786 } 15787 case 'blockquote': { 15788 if ($this->rtl) { 15789 $this->rMargin += $this->listindent; 15790 } else { 15791 $this->lMargin += $this->listindent; 15792 } 15793 ++$this->listindentlevel; 15794 $this->addHTMLVertSpace($hbz, $hb, $cell, $firstorlast); 15795 break; 15796 } 15797 case 'br': { 15798 $this->addHTMLVertSpace($hbz, 0, $cell, $firstorlast); 15799 break; 15800 } 15801 case 'div': { 15802 $this->addHTMLVertSpace($hbz, 0, $cell, $firstorlast); 15803 break; 15804 } 15805 case 'p': { 15806 $this->addHTMLVertSpace($hbz, $hb, $cell, $firstorlast); 15807 break; 15808 } 15809 case 'pre': { 15810 $this->addHTMLVertSpace($hbz, $hb, $cell, $firstorlast); 15811 $this->premode = true; 15812 break; 15813 } 15814 case 'sup': { 15815 $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k)); 15816 break; 15817 } 15818 case 'sub': { 15819 $this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k)); 15820 break; 15821 } 15822 case 'h1': 15823 case 'h2': 15824 case 'h3': 15825 case 'h4': 15826 case 'h5': 15827 case 'h6': { 15828 $this->addHTMLVertSpace($hbz, $hb, $cell, $firstorlast); 15829 break; 15830 } 15831 // Form fields (since 4.8.000 - 2009-09-07) 15832 case 'form': { 15833 if (isset($tag['attribute']['action'])) { 15834 $this->form_action = $tag['attribute']['action']; 15835 } else { 15836 $this->form_action = K_PATH_URL.$_SERVER['SCRIPT_NAME']; 15837 } 15838 if (isset($tag['attribute']['enctype'])) { 15839 $this->form_enctype = $tag['attribute']['enctype']; 15840 } else { 15841 $this->form_enctype = 'application/x-www-form-urlencoded'; 15842 } 15843 if (isset($tag['attribute']['method'])) { 15844 $this->form_mode = $tag['attribute']['method']; 15845 } else { 15846 $this->form_mode = 'post'; 15847 } 15848 break; 15849 } 15850 case 'input': { 15851 if (isset($tag['attribute']['name']) AND !$this->empty_string($tag['attribute']['name'])) { 15852 $name = $tag['attribute']['name']; 15853 } else { 15854 break; 15855 } 15856 $prop = array(); 15857 $opt = array(); 15858 if (isset($tag['attribute']['value']) AND !$this->empty_string($tag['attribute']['value'])) { 15859 $value = $tag['attribute']['value']; 15860 } 15861 if (isset($tag['attribute']['maxlength']) AND !$this->empty_string($tag['attribute']['maxlength'])) { 15862 $opt['maxlen'] = intval($tag['attribute']['value']); 15863 } 15864 $h = $this->FontSize * $this->cell_height_ratio; 15865 if (isset($tag['attribute']['size']) AND !$this->empty_string($tag['attribute']['size'])) { 15866 $w = intval($tag['attribute']['size']) * $this->GetStringWidth(chr(32)) * 2; 15867 } else { 15868 $w = $h; 15869 } 15870 if (isset($tag['attribute']['checked']) AND (($tag['attribute']['checked'] == 'checked') OR ($tag['attribute']['checked'] == 'true'))) { 15871 $checked = true; 15872 } else { 15873 $checked = false; 15874 } 15875 switch ($tag['attribute']['type']) { 15876 case 'text': { 15877 if (isset($value)) { 15878 $opt['v'] = $value; 15879 } 15880 $this->TextField($name, $w, $h, $prop, $opt, '', '', false); 15881 break; 15882 } 15883 case 'password': { 15884 if (isset($value)) { 15885 $opt['v'] = $value; 15886 } 15887 $prop['password'] = 'true'; 15888 $this->TextField($name, $w, $h, $prop, $opt, '', '', false); 15889 break; 15890 } 15891 case 'checkbox': { 15892 $this->CheckBox($name, $w, $checked, $prop, $opt, $value, '', '', false); 15893 break; 15894 } 15895 case 'radio': { 15896 $this->RadioButton($name, $w, $prop, $opt, $value, $checked, '', '', false); 15897 break; 15898 } 15899 case 'submit': { 15900 $w = $this->GetStringWidth($value) * 1.5; 15901 $h *= 1.6; 15902 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255)); 15903 $action = array(); 15904 $action['S'] = 'SubmitForm'; 15905 $action['F'] = $this->form_action; 15906 if ($this->form_enctype != 'FDF') { 15907 $action['Flags'] = array('ExportFormat'); 15908 } 15909 if ($this->form_mode == 'get') { 15910 $action['Flags'] = array('GetMethod'); 15911 } 15912 $this->Button($name, $w, $h, $value, $action, $prop, $opt, '', '', false); 15913 break; 15914 } 15915 case 'reset': { 15916 $w = $this->GetStringWidth($value) * 1.5; 15917 $h *= 1.6; 15918 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255)); 15919 $this->Button($name, $w, $h, $value, array('S'=>'ResetForm'), $prop, $opt, '', '', false); 15920 break; 15921 } 15922 case 'file': { 15923 $prop['fileSelect'] = 'true'; 15924 $this->TextField($name, $w, $h, $prop, $opt, '', '', false); 15925 if (!isset($value)) { 15926 $value = '*'; 15927 } 15928 $w = $this->GetStringWidth($value) * 2; 15929 $h *= 1.2; 15930 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255)); 15931 $jsaction = 'var f=this.getField(\''.$name.'\'); f.browseForFileToSubmit();'; 15932 $this->Button('FB_'.$name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false); 15933 break; 15934 } 15935 case 'hidden': { 15936 if (isset($value)) { 15937 $opt['v'] = $value; 15938 } 15939 $opt['f'] = array('invisible', 'hidden'); 15940 $this->TextField($name, 0, 0, $prop, $opt, '', '', false); 15941 break; 15942 } 15943 case 'image': { 15944 // THIS TYPE MUST BE FIXED 15945 if (isset($tag['attribute']['src']) AND !$this->empty_string($tag['attribute']['src'])) { 15946 $img = $tag['attribute']['src']; 15947 } else { 15948 break; 15949 } 15950 $value = 'img'; 15951 //$opt['mk'] = array('i'=>$img, 'tp'=>1, 'if'=>array('sw'=>'A', 's'=>'A', 'fb'=>false)); 15952 if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) { 15953 $jsaction = $tag['attribute']['onclick']; 15954 } else { 15955 $jsaction = ''; 15956 } 15957 $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false); 15958 break; 15959 } 15960 case 'button': { 15961 $w = $this->GetStringWidth($value) * 1.5; 15962 $h *= 1.6; 15963 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255)); 15964 if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) { 15965 $jsaction = $tag['attribute']['onclick']; 15966 } else { 15967 $jsaction = ''; 15968 } 15969 $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false); 15970 break; 15971 } 15972 } 15973 break; 15974 } 15975 case 'textarea': { 15976 $prop = array(); 15977 $opt = array(); 15978 if (isset($tag['attribute']['name']) AND !$this->empty_string($tag['attribute']['name'])) { 15979 $name = $tag['attribute']['name']; 15980 } else { 15981 break; 15982 } 15983 if (isset($tag['attribute']['value']) AND !$this->empty_string($tag['attribute']['value'])) { 15984 $opt['v'] = $tag['attribute']['value']; 15985 } 15986 if (isset($tag['attribute']['cols']) AND !$this->empty_string($tag['attribute']['cols'])) { 15987 $w = intval($tag['attribute']['cols']) * $this->GetStringWidth(chr(32)) * 2; 15988 } else { 15989 $w = 40; 15990 } 15991 if (isset($tag['attribute']['rows']) AND !$this->empty_string($tag['attribute']['rows'])) { 15992 $h = intval($tag['attribute']['rows']) * $this->FontSize * $this->cell_height_ratio; 15993 } else { 15994 $h = 10; 15995 } 15996 $prop['multiline'] = 'true'; 15997 $this->TextField($name, $w, $h, $prop, $opt, '', '', false); 15998 break; 15999 } 16000 case 'select': { 16001 $h = $this->FontSize * $this->cell_height_ratio; 16002 if (isset($tag['attribute']['size']) AND !$this->empty_string($tag['attribute']['size'])) { 16003 $h *= ($tag['attribute']['size'] + 1); 16004 } 16005 $prop = array(); 16006 $opt = array(); 16007 if (isset($tag['attribute']['name']) AND !$this->empty_string($tag['attribute']['name'])) { 16008 $name = $tag['attribute']['name']; 16009 } else { 16010 break; 16011 } 16012 $w = 0; 16013 if (isset($tag['attribute']['opt']) AND !$this->empty_string($tag['attribute']['opt'])) { 16014 $options = explode ("\r", $tag['attribute']['opt']); 16015 $values = array(); 16016 foreach ($options as $val) { 16017 if (strpos($val, "\t") !== false) { 16018 $opts = explode("\t", $val); 16019 $values[] = $opts; 16020 $w = max($w, $this->GetStringWidth($opts[1])); 16021 } else { 16022 $values[] = $val; 16023 $w = max($w, $this->GetStringWidth($val)); 16024 } 16025 } 16026 } else { 16027 break; 16028 } 16029 $w *= 2; 16030 if (isset($tag['attribute']['multiple']) AND ($tag['attribute']['multiple']='multiple')) { 16031 $prop['multipleSelection'] = 'true'; 16032 $this->ListBox($name, $w, $h, $values, $prop, $opt, '', '', false); 16033 } else { 16034 $this->ComboBox($name, $w, $h, $values, $prop, $opt, '', '', false); 16035 } 16036 break; 16037 } 16038 case 'tcpdf': { 16039 if (defined('K_TCPDF_CALLS_IN_HTML') AND (K_TCPDF_CALLS_IN_HTML === true)) { 16040 // Special tag used to call TCPDF methods 16041 if (isset($tag['attribute']['method'])) { 16042 $tcpdf_method = $tag['attribute']['method']; 16043 if (method_exists($this, $tcpdf_method)) { 16044 if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) { 16045 $params = unserialize(urldecode($tag['attribute']['params'])); 16046 call_user_func_array(array($this, $tcpdf_method), $params); 16047 } else { 16048 $this->$tcpdf_method(); 16049 } 16050 $this->newline = true; 16051 } 16052 } 16053 } 16054 break; 16055 } 16056 default: { 16057 break; 16058 } 16059 } 16060 if ($dom[$key]['self'] AND isset($dom[$key]['attribute']['pagebreakafter'])) { 16061 $pba = $dom[$key]['attribute']['pagebreakafter']; 16062 // check for pagebreak 16063 if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) { 16064 // add a page (or trig AcceptPageBreak() for multicolumn mode) 16065 $this->checkPageBreak($this->PageBreakTrigger + 1); 16066 } 16067 if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0)))) 16068 OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) { 16069 // add a page (or trig AcceptPageBreak() for multicolumn mode) 16070 $this->checkPageBreak($this->PageBreakTrigger + 1); 16071 } 16072 } 16073 } 16074 16075 /** 16076 * Process closing tags. 16077 * @param array $dom html dom array 16078 * @param int $key current element id 16079 * @param boolean $cell if true add the default cMargin space to each new line (default false). 16080 * @param int $maxbottomliney maximum y value of current line 16081 * @access protected 16082 */ 16083 protected function closeHTMLTagHandler(&$dom, $key, $cell, $maxbottomliney=0) { 16084 $tag = $dom[$key]; 16085 $parent = $dom[($dom[$key]['parent'])]; 16086 $firstorlast = ((!isset($dom[($key + 1)])) OR ((!isset($dom[($key + 2)])) AND ($dom[($key + 1)]['value'] == 'marker'))); 16087 $in_table_head = false; 16088 if ($tag['block']) { 16089 $hbz = 0; // distance from y to line bottom 16090 $hb = 0; // vertical space between block tags 16091 // calculate vertical space for block tags 16092 if (isset($this->tagvspaces[$tag['value']][1]['h']) AND ($this->tagvspaces[$tag['value']][1]['h'] >= 0)) { 16093 $pre_h = $this->tagvspaces[$tag['value']][1]['h']; 16094 } elseif (isset($parent['fontsize'])) { 16095 $pre_h = (($parent['fontsize'] / $this->k) * $this->cell_height_ratio); 16096 } else { 16097 $pre_h = $this->FontSize * $this->cell_height_ratio; 16098 } 16099 if (isset($this->tagvspaces[$tag['value']][1]['n'])) { 16100 $n = $this->tagvspaces[$tag['value']][1]['n']; 16101 } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) { 16102 $n = 0.6; 16103 } else { 16104 $n = 1; 16105 } 16106 $hb = ($n * $pre_h); 16107 if ($this->y < $maxbottomliney) { 16108 $hbz = ($maxbottomliney - $this->y); 16109 } 16110 } 16111 //Closing tag 16112 switch($tag['value']) { 16113 case 'tr': { 16114 $table_el = $dom[($dom[$key]['parent'])]['parent']; 16115 if (!isset($parent['endy'])) { 16116 $dom[($dom[$key]['parent'])]['endy'] = $this->y; 16117 $parent['endy'] = $this->y; 16118 } 16119 if (!isset($parent['endpage'])) { 16120 $dom[($dom[$key]['parent'])]['endpage'] = $this->page; 16121 $parent['endpage'] = $this->page; 16122 } 16123 // update row-spanned cells 16124 if (isset($dom[$table_el]['rowspans'])) { 16125 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) { 16126 $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1; 16127 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) { 16128 if ($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) { 16129 $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']); 16130 } elseif ($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) { 16131 $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy']; 16132 $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage']; 16133 } 16134 } 16135 } 16136 // report new endy and endpage to the rowspanned cells 16137 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) { 16138 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) { 16139 $dom[$table_el]['rowspans'][$k]['endpage'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']); 16140 $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage']; 16141 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']); 16142 $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy']; 16143 } 16144 } 16145 // update remaining rowspanned cells 16146 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) { 16147 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) { 16148 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage']; 16149 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy']; 16150 } 16151 } 16152 } 16153 if (($this->num_columns > 1) AND ($dom[($dom[$key]['parent'])]['endy'] >= ($this->PageBreakTrigger - $this->lasth)) AND ($this->y < $dom[($dom[$key]['parent'])]['endy'])) { 16154 $this->Ln(0, $cell); 16155 } else { 16156 $this->setPage($dom[($dom[$key]['parent'])]['endpage']); 16157 $this->y = $dom[($dom[$key]['parent'])]['endy']; 16158 if (isset($dom[$table_el]['attribute']['cellspacing'])) { 16159 $cellspacing = $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px'); 16160 $this->y += $cellspacing; 16161 } 16162 $this->Ln(0, $cell); 16163 $this->x = $parent['startx']; 16164 // account for booklet mode 16165 if ($this->page > $parent['startpage']) { 16166 if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$parent['startpage']]['orm'])) { 16167 $this->x -= ($this->pagedim[$this->page]['orm'] - $this->pagedim[$parent['startpage']]['orm']); 16168 } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$parent['startpage']]['olm'])) { 16169 $this->x += ($this->pagedim[$this->page]['olm'] - $this->pagedim[$parent['startpage']]['olm']); 16170 } 16171 } 16172 } 16173 break; 16174 } 16175 case 'tablehead': 16176 // closing tag used for the thead part 16177 $in_table_head = true; 16178 $this->inthead = false; 16179 case 'table': { 16180 $table_el = $parent; 16181 // draw borders 16182 if ((isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0)) 16183 OR (isset($table_el['style']['border']) AND ($table_el['style']['border'] > 0))) { 16184 $border = 1; 16185 } else { 16186 $border = 0; 16187 } 16188 // fix bottom line alignment of last line before page break 16189 foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) { 16190 // update row-spanned cells 16191 if (isset($dom[($dom[$key]['parent'])]['rowspans'])) { 16192 foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) { 16193 if ($trwsp['trid'] == $trkey) { 16194 $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1; 16195 } 16196 if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] >= 0)) { 16197 $dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey; 16198 } 16199 } 16200 } 16201 if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) { 16202 $pgendy = $this->pagedim[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim[$dom[$prevtrkey]['endpage']]['bm']; 16203 $dom[$prevtrkey]['endy'] = $pgendy; 16204 // update row-spanned cells 16205 if (isset($dom[($dom[$key]['parent'])]['rowspans'])) { 16206 foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) { 16207 if (($trwsp['trid'] == $trkey) AND ($trwsp['mrowspan'] > 1) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) { 16208 $dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy; 16209 $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1; 16210 } 16211 } 16212 } 16213 } 16214 $prevtrkey = $trkey; 16215 $table_el = $dom[($dom[$key]['parent'])]; 16216 } 16217 // for each row 16218 foreach ($table_el['trids'] as $j => $trkey) { 16219 $parent = $dom[$trkey]; 16220 // for each cell on the row 16221 foreach ($parent['cellpos'] as $k => $cellpos) { 16222 if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) { 16223 $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx']; 16224 $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx']; 16225 $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy']; 16226 $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage']; 16227 $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage']; 16228 } else { 16229 $endy = $parent['endy']; 16230 $startpage = $parent['startpage']; 16231 $endpage = $parent['endpage']; 16232 } 16233 if ($endpage > $startpage) { 16234 // design borders around HTML cells. 16235 for ($page=$startpage; $page <= $endpage; ++$page) { 16236 $this->setPage($page); 16237 if ($page == $startpage) { 16238 $this->y = $parent['starty']; // put cursor at the beginning of row on the first page 16239 $ch = $this->getPageHeight() - $parent['starty'] - $this->getBreakMargin(); 16240 $cborder = $this->getBorderMode($border, $position='start'); 16241 } elseif ($page == $endpage) { 16242 $this->y = $this->tMargin; // put cursor at the beginning of last page 16243 $ch = $endy - $this->tMargin; 16244 $cborder = $this->getBorderMode($border, $position='end'); 16245 } else { 16246 $this->y = $this->tMargin; // put cursor at the beginning of the current page 16247 $ch = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin(); 16248 $cborder = $this->getBorderMode($border, $position='middle'); 16249 } 16250 if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) { 16251 $this->SetFillColorArray($cellpos['bgcolor']); 16252 $fill = true; 16253 } else { 16254 $fill = false; 16255 } 16256 $cw = abs($cellpos['endx'] - $cellpos['startx']); 16257 $this->x = $cellpos['startx']; 16258 // account for margin changes 16259 if ($page > $startpage) { 16260 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) { 16261 $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']); 16262 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['lm'] != $this->pagedim[$startpage]['olm'])) { 16263 $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']); 16264 } 16265 } 16266 // design a cell around the text 16267 $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, '', $cborder, 1, '', $fill, '', 0, true); 16268 if ($cborder OR $fill) { 16269 $pagebuff = $this->getPageBuffer($this->page); 16270 $pstart = substr($pagebuff, 0, $this->intmrk[$this->page]); 16271 $pend = substr($pagebuff, $this->intmrk[$this->page]); 16272 $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend); 16273 $this->intmrk[$this->page] += strlen($ccode."\n"); 16274 } 16275 } 16276 } else { 16277 $this->setPage($startpage); 16278 if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) { 16279 $this->SetFillColorArray($cellpos['bgcolor']); 16280 $fill = true; 16281 } else { 16282 $fill = false; 16283 } 16284 $this->x = $cellpos['startx']; 16285 $this->y = $parent['starty']; 16286 $cw = abs($cellpos['endx'] - $cellpos['startx']); 16287 $ch = $endy - $parent['starty']; 16288 // design a cell around the text 16289 $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, '', $border, 1, '', $fill, '', 0, true); 16290 if ($border OR $fill) { 16291 if (end($this->transfmrk[$this->page]) !== false) { 16292 $pagemarkkey = key($this->transfmrk[$this->page]); 16293 $pagemark = &$this->transfmrk[$this->page][$pagemarkkey]; 16294 } elseif ($this->InFooter) { 16295 $pagemark = &$this->footerpos[$this->page]; 16296 } else { 16297 $pagemark = &$this->intmrk[$this->page]; 16298 } 16299 $pagebuff = $this->getPageBuffer($this->page); 16300 $pstart = substr($pagebuff, 0, $pagemark); 16301 $pend = substr($pagebuff, $pagemark); 16302 $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend); 16303 $pagemark += strlen($ccode."\n"); 16304 } 16305 } 16306 } 16307 if (isset($table_el['attribute']['cellspacing'])) { 16308 $cellspacing = $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px'); 16309 $this->y += $cellspacing; 16310 } 16311 $this->Ln(0, $cell); 16312 $this->x = $parent['startx']; 16313 if ($endpage > $startpage) { 16314 if (($this->rtl) AND ($this->pagedim[$endpage]['orm'] != $this->pagedim[$startpage]['orm'])) { 16315 $this->x += ($this->pagedim[$endpage]['orm'] - $this->pagedim[$startpage]['orm']); 16316 } elseif ((!$this->rtl) AND ($this->pagedim[$endpage]['olm'] != $this->pagedim[$startpage]['olm'])) { 16317 $this->x += ($this->pagedim[$endpage]['olm'] - $this->pagedim[$startpage]['olm']); 16318 } 16319 } 16320 } 16321 if (!$in_table_head) { 16322 // we are not inside a thead section 16323 if (isset($parent['cellpadding'])) { 16324 $this->cMargin = $this->oldcMargin; 16325 } 16326 $this->lasth = $this->FontSize * $this->cell_height_ratio; 16327 if (isset($this->theadMargins['top'])) { 16328 if (($this->theadMargins['top'] == $this->tMargin) AND ($this->page == ($this->numpages - 1))) { 16329 // remove last page containing only THEAD 16330 $this->deletePage($this->numpages); 16331 } 16332 // restore top margin 16333 $this->tMargin = $this->theadMargins['top']; 16334 $this->pagedim[$this->page]['tm'] = $this->tMargin; 16335 } 16336 if (!isset($table_el['attribute']['nested']) OR ($table_el['attribute']['nested'] != 'true')) { 16337 // reset main table header 16338 $this->thead = ''; 16339 $this->theadMargins = array(); 16340 } 16341 } 16342 break; 16343 } 16344 case 'a': { 16345 $this->HREF = ''; 16346 break; 16347 } 16348 case 'sup': { 16349 $this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k)); 16350 break; 16351 } 16352 case 'sub': { 16353 $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize'])/$this->k)); 16354 break; 16355 } 16356 case 'div': { 16357 $this->addHTMLVertSpace($hbz, 0, $cell, $firstorlast); 16358 break; 16359 } 16360 case 'blockquote': { 16361 if ($this->rtl) { 16362 $this->rMargin -= $this->listindent; 16363 } else { 16364 $this->lMargin -= $this->listindent; 16365 } 16366 --$this->listindentlevel; 16367 $this->addHTMLVertSpace($hbz, $hb, $cell, $firstorlast); 16368 break; 16369 } 16370 case 'p': { 16371 $this->addHTMLVertSpace($hbz, $hb, $cell, $firstorlast); 16372 break; 16373 } 16374 case 'pre': { 16375 $this->addHTMLVertSpace($hbz, $hb, $cell, $firstorlast); 16376 $this->premode = false; 16377 break; 16378 } 16379 case 'dl': { 16380 --$this->listnum; 16381 if ($this->listnum <= 0) { 16382 $this->listnum = 0; 16383 $this->addHTMLVertSpace($hbz, $hb, $cell, $firstorlast); 16384 } else { 16385 $this->addHTMLVertSpace(0, 0, $cell, $firstorlast); 16386 } 16387 $this->lasth = $this->FontSize * $this->cell_height_ratio; 16388 break; 16389 } 16390 case 'dt': { 16391 $this->lispacer = ''; 16392 $this->addHTMLVertSpace(0, 0, $cell, $firstorlast); 16393 break; 16394 } 16395 case 'dd': { 16396 $this->lispacer = ''; 16397 if ($this->rtl) { 16398 $this->rMargin -= $this->listindent; 16399 } else { 16400 $this->lMargin -= $this->listindent; 16401 } 16402 --$this->listindentlevel; 16403 $this->addHTMLVertSpace(0, 0, $cell, $firstorlast); 16404 break; 16405 } 16406 case 'ul': 16407 case 'ol': { 16408 --$this->listnum; 16409 $this->lispacer = ''; 16410 if ($this->rtl) { 16411 $this->rMargin -= $this->listindent; 16412 } else { 16413 $this->lMargin -= $this->listindent; 16414 } 16415 --$this->listindentlevel; 16416 if ($this->listnum <= 0) { 16417 $this->listnum = 0; 16418 $this->addHTMLVertSpace($hbz, $hb, $cell, $firstorlast); 16419 } else { 16420 $this->addHTMLVertSpace(0, 0, $cell, $firstorlast); 16421 } 16422 $this->lasth = $this->FontSize * $this->cell_height_ratio; 16423 break; 16424 } 16425 case 'li': { 16426 $this->lispacer = ''; 16427 $this->addHTMLVertSpace(0, 0, $cell, $firstorlast); 16428 break; 16429 } 16430 case 'h1': 16431 case 'h2': 16432 case 'h3': 16433 case 'h4': 16434 case 'h5': 16435 case 'h6': { 16436 $this->addHTMLVertSpace($hbz, $hb, $cell, $firstorlast); 16437 break; 16438 } 16439 // Form fields (since 4.8.000 - 2009-09-07) 16440 case 'form': { 16441 $this->form_action = ''; 16442 $this->form_enctype = 'application/x-www-form-urlencoded'; 16443 break; 16444 } 16445 default : { 16446 break; 16447 } 16448 } 16449 if (isset($dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'])) { 16450 $pba = $dom[($dom[$key]['parent'])]['attribute']['pagebreakafter']; 16451 // check for pagebreak 16452 if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) { 16453 // add a page (or trig AcceptPageBreak() for multicolumn mode) 16454 $this->checkPageBreak($this->PageBreakTrigger + 1); 16455 } 16456 if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0)))) 16457 OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) { 16458 // add a page (or trig AcceptPageBreak() for multicolumn mode) 16459 $this->checkPageBreak($this->PageBreakTrigger + 1); 16460 } 16461 } 16462 $this->tmprtl = false; 16463 } 16464 16465 /** 16466 * Add vertical spaces if needed. 16467 * @param string $hbz Distance between current y and line bottom. 16468 * @param string $hb The height of the break. 16469 * @param boolean $cell if true add the default cMargin space to each new line (default false). 16470 * @param boolean $firstorlast if true do not print additional empty lines. 16471 * @access protected 16472 */ 16473 protected function addHTMLVertSpace($hbz=0, $hb=0, $cell=false, $firstorlast=false) { 16474 if ($firstorlast) { 16475 $this->Ln(0, $cell); 16476 $this->htmlvspace = 0; 16477 return; 16478 } 16479 if ($hb < $this->htmlvspace) { 16480 $hd = 0; 16481 } else { 16482 $hd = $hb - $this->htmlvspace; 16483 $this->htmlvspace = $hb; 16484 } 16485 $this->Ln(($hbz + $hd), $cell); 16486 } 16487 16488 /** 16489 * Set the default bullet to be used as LI bullet symbol 16490 * @param string $symbol character or string to be used (legal values are: '' = automatic, '!' = auto bullet, '#' = auto numbering, 'disc', 'disc', 'circle', 'square', '1', 'decimal', 'decimal-leading-zero', 'i', 'lower-roman', 'I', 'upper-roman', 'a', 'lower-alpha', 'lower-latin', 'A', 'upper-alpha', 'upper-latin', 'lower-greek') 16491 * @access public 16492 * @since 4.0.028 (2008-09-26) 16493 */ 16494 public function setLIsymbol($symbol='!') { 16495 $symbol = strtolower($symbol); 16496 switch ($symbol) { 16497 case '!' : 16498 case '#' : 16499 case 'disc' : 16500 case 'circle' : 16501 case 'square' : 16502 case '1': 16503 case 'decimal': 16504 case 'decimal-leading-zero': 16505 case 'i': 16506 case 'lower-roman': 16507 case 'I': 16508 case 'upper-roman': 16509 case 'a': 16510 case 'lower-alpha': 16511 case 'lower-latin': 16512 case 'A': 16513 case 'upper-alpha': 16514 case 'upper-latin': 16515 case 'lower-greek': { 16516 $this->lisymbol = $symbol; 16517 break; 16518 } 16519 default : { 16520 $this->lisymbol = ''; 16521 } 16522 } 16523 } 16524 16525 /** 16526 * Set the booklet mode for double-sided pages. 16527 * @param boolean $booklet true set the booklet mode on, fals eotherwise. 16528 * @param float $inner Inner page margin. 16529 * @param float $outer Outer page margin. 16530 * @access public 16531 * @since 4.2.000 (2008-10-29) 16532 */ 16533 public function SetBooklet($booklet=true, $inner=-1, $outer=-1) { 16534 $this->booklet = $booklet; 16535 if ($inner >= 0) { 16536 $this->lMargin = $inner; 16537 } 16538 if ($outer >= 0) { 16539 $this->rMargin = $outer; 16540 } 16541 } 16542 16543 /** 16544 * Swap the left and right margins. 16545 * @param boolean $reverse if true swap left and right margins. 16546 * @access protected 16547 * @since 4.2.000 (2008-10-29) 16548 */ 16549 protected function swapMargins($reverse=true) { 16550 if ($reverse) { 16551 // swap left and right margins 16552 $mtemp = $this->original_lMargin; 16553 $this->original_lMargin = $this->original_rMargin; 16554 $this->original_rMargin = $mtemp; 16555 $deltam = $this->original_lMargin - $this->original_rMargin; 16556 $this->lMargin += $deltam; 16557 $this->rMargin -= $deltam; 16558 } 16559 } 16560 16561 /** 16562 * Set the vertical spaces for HTML tags. 16563 * The array must have the following structure (example): 16564 * $tagvs = array('h1' => array(0 => array('h' => '', 'n' => 2), 1 => array('h' => 1.3, 'n' => 1))); 16565 * The first array level contains the tag names, 16566 * the second level contains 0 for opening tags or 1 for closing tags, 16567 * the third level contains the vertical space unit (h) and the number spaces to add (n). 16568 * If the h parameter is not specified, default values are used. 16569 * @param array $tagvs array of tags and relative vertical spaces. 16570 * @access public 16571 * @since 4.2.001 (2008-10-30) 16572 */ 16573 public function setHtmlVSpace($tagvs) { 16574 $this->tagvspaces = $tagvs; 16575 } 16576 16577 /** 16578 * Set custom width for list indentation. 16579 * @param float $width width of the indentation. Use negative value to disable it. 16580 * @access public 16581 * @since 4.2.007 (2008-11-12) 16582 */ 16583 public function setListIndentWidth($width) { 16584 return $this->customlistindent = floatval($width); 16585 } 16586 16587 /** 16588 * Set the top/bottom cell sides to be open or closed when the cell cross the page. 16589 * @param boolean $isopen if true keeps the top/bottom border open for the cell sides that cross the page. 16590 * @access public 16591 * @since 4.2.010 (2008-11-14) 16592 */ 16593 public function setOpenCell($isopen) { 16594 $this->opencell = $isopen; 16595 } 16596 16597 /** 16598 * Set the color and font style for HTML links. 16599 * @param array $color RGB array of colors 16600 * @param string $fontstyle additional font styles to add 16601 * @access public 16602 * @since 4.4.003 (2008-12-09) 16603 */ 16604 public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') { 16605 $this->htmlLinkColorArray = $color; 16606 $this->htmlLinkFontStyle = $fontstyle; 16607 } 16608 16609 /** 16610 * Convert HTML string containing value and unit of measure to user's units or points. 16611 * @param string $htmlval string containing values and unit 16612 * @param string $refsize reference value in points 16613 * @param string $defaultunit default unit (can be one of the following: %, em, ex, px, in, mm, pc, pt). 16614 * @param boolean $point if true returns points, otherwise returns value in user's units 16615 * @return float value in user's unit or point if $points=true 16616 * @access public 16617 * @since 4.4.004 (2008-12-10) 16618 */ 16619 public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) { 16620 $supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt'); 16621 $retval = 0; 16622 $value = 0; 16623 $unit = 'px'; 16624 $k = $this->k; 16625 if ($points) { 16626 $k = 1; 16627 } 16628 if (in_array($defaultunit, $supportedunits)) { 16629 $unit = $defaultunit; 16630 } 16631 if (is_numeric($htmlval)) { 16632 $value = floatval($htmlval); 16633 } elseif (preg_match('/([0-9\.\-\+]+)/', $htmlval, $mnum)) { 16634 $value = floatval($mnum[1]); 16635 if (preg_match('/([a-z%]+)/', $htmlval, $munit)) { 16636 if (in_array($munit[1], $supportedunits)) { 16637 $unit = $munit[1]; 16638 } 16639 } 16640 } 16641 switch ($unit) { 16642 // percentage 16643 case '%': { 16644 $retval = (($value * $refsize) / 100); 16645 break; 16646 } 16647 // relative-size 16648 case 'em': { 16649 $retval = ($value * $refsize); 16650 break; 16651 } 16652 case 'ex': { 16653 $retval = $value * ($refsize / 2); 16654 break; 16655 } 16656 // absolute-size 16657 case 'in': { 16658 $retval = ($value * $this->dpi) / $k; 16659 break; 16660 } 16661 case 'cm': { 16662 $retval = ($value / 2.54 * $this->dpi) / $k; 16663 break; 16664 } 16665 case 'mm': { 16666 $retval = ($value / 25.4 * $this->dpi) / $k; 16667 break; 16668 } 16669 case 'pc': { 16670 // one pica is 12 points 16671 $retval = ($value * 12) / $k; 16672 break; 16673 } 16674 case 'pt': { 16675 $retval = $value / $k; 16676 break; 16677 } 16678 case 'px': { 16679 $retval = $this->pixelsToUnits($value); 16680 break; 16681 } 16682 } 16683 return $retval; 16684 } 16685 16686 /** 16687 * Returns the Roman representation of an integer number 16688 * @param int number to convert 16689 * @return string roman representation of the specified number 16690 * @access public 16691 * @since 4.4.004 (2008-12-10) 16692 */ 16693 public function intToRoman($number) { 16694 $roman = ''; 16695 while ($number >= 1000) { 16696 $roman .= 'M'; 16697 $number -= 1000; 16698 } 16699 while ($number >= 900) { 16700 $roman .= 'CM'; 16701 $number -= 900; 16702 } 16703 while ($number >= 500) { 16704 $roman .= 'D'; 16705 $number -= 500; 16706 } 16707 while ($number >= 400) { 16708 $roman .= 'CD'; 16709 $number -= 400; 16710 } 16711 while ($number >= 100) { 16712 $roman .= 'C'; 16713 $number -= 100; 16714 } 16715 while ($number >= 90) { 16716 $roman .= 'XC'; 16717 $number -= 90; 16718 } 16719 while ($number >= 50) { 16720 $roman .= 'L'; 16721 $number -= 50; 16722 } 16723 while ($number >= 40) { 16724 $roman .= 'XL'; 16725 $number -= 40; 16726 } 16727 while ($number >= 10) { 16728 $roman .= 'X'; 16729 $number -= 10; 16730 } 16731 while ($number >= 9) { 16732 $roman .= 'IX'; 16733 $number -= 9; 16734 } 16735 while ($number >= 5) { 16736 $roman .= 'V'; 16737 $number -= 5; 16738 } 16739 while ($number >= 4) { 16740 $roman .= 'IV'; 16741 $number -= 4; 16742 } 16743 while ($number >= 1) { 16744 $roman .= 'I'; 16745 --$number; 16746 } 16747 return $roman; 16748 } 16749 16750 /** 16751 * Output an HTML list bullet or ordered item symbol 16752 * @param int $listdepth list nesting level 16753 * @param string $listtype type of list 16754 * @param float $size current font size 16755 * @access protected 16756 * @since 4.4.004 (2008-12-10) 16757 */ 16758 protected function putHtmlListBullet($listdepth, $listtype='', $size=10) { 16759 $size /= $this->k; 16760 $fill = ''; 16761 $color = $this->fgcolor; 16762 $width = 0; 16763 $textitem = ''; 16764 $tmpx = $this->x; 16765 $lspace = $this->GetStringWidth(' '); 16766 if ($listtype == '!') { 16767 // set default list type for unordered list 16768 $deftypes = array('disc', 'circle', 'square'); 16769 $listtype = $deftypes[($listdepth - 1) % 3]; 16770 } elseif ($listtype == '#') { 16771 // set default list type for ordered list 16772 $listtype = 'decimal'; 16773 } 16774 switch ($listtype) { 16775 // unordered types 16776 case 'none': { 16777 break; 16778 } 16779 case 'disc': { 16780 $fill = 'F'; 16781 } 16782 case 'circle': { 16783 $fill .= 'D'; 16784 $r = $size / 6; 16785 $lspace += (2 * $r); 16786 if ($this->rtl) { 16787 $this->x += $lspace; 16788 } else { 16789 $this->x -= $lspace; 16790 } 16791 $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), $r, 0, 360, $fill, array('color'=>$color), $color, 8); 16792 break; 16793 } 16794 case 'square': { 16795 $l = $size / 3; 16796 $lspace += $l; 16797 if ($this->rtl) {; 16798 $this->x += $lspace; 16799 } else { 16800 $this->x -= $lspace; 16801 } 16802 $this->Rect($this->x, ($this->y + (($this->lasth - $l)/ 2)), $l, $l, 'F', array(), $color); 16803 break; 16804 } 16805 // ordered types 16806 // $this->listcount[$this->listnum]; 16807 // $textitem 16808 case '1': 16809 case 'decimal': { 16810 $textitem = $this->listcount[$this->listnum]; 16811 break; 16812 } 16813 case 'decimal-leading-zero': { 16814 $textitem = sprintf('%02d', $this->listcount[$this->listnum]); 16815 break; 16816 } 16817 case 'i': 16818 case 'lower-roman': { 16819 $textitem = strtolower($this->intToRoman($this->listcount[$this->listnum])); 16820 break; 16821 } 16822 case 'I': 16823 case 'upper-roman': { 16824 $textitem = $this->intToRoman($this->listcount[$this->listnum]); 16825 break; 16826 } 16827 case 'a': 16828 case 'lower-alpha': 16829 case 'lower-latin': { 16830 $textitem = chr(97 + $this->listcount[$this->listnum] - 1); 16831 break; 16832 } 16833 case 'A': 16834 case 'upper-alpha': 16835 case 'upper-latin': { 16836 $textitem = chr(65 + $this->listcount[$this->listnum] - 1); 16837 break; 16838 } 16839 case 'lower-greek': { 16840 $textitem = $this->unichr(945 + $this->listcount[$this->listnum] - 1); 16841 break; 16842 } 16843 /* 16844 // Types to be implemented (special handling) 16845 case 'hebrew': { 16846 break; 16847 } 16848 case 'armenian': { 16849 break; 16850 } 16851 case 'georgian': { 16852 break; 16853 } 16854 case 'cjk-ideographic': { 16855 break; 16856 } 16857 case 'hiragana': { 16858 break; 16859 } 16860 case 'katakana': { 16861 break; 16862 } 16863 case 'hiragana-iroha': { 16864 break; 16865 } 16866 case 'katakana-iroha': { 16867 break; 16868 } 16869 */ 16870 default: { 16871 $textitem = $this->listcount[$this->listnum]; 16872 } 16873 } 16874 if (!$this->empty_string($textitem)) { 16875 // print ordered item 16876 if ($this->rtl) { 16877 $textitem = '.'.$textitem; 16878 } else { 16879 $textitem = $textitem.'.'; 16880 } 16881 $lspace += $this->GetStringWidth($textitem); 16882 if ($this->rtl) { 16883 $this->x += $lspace; 16884 } else { 16885 $this->x -= $lspace; 16886 } 16887 $this->Write($this->lasth, $textitem, '', false, '', false, 0, false); 16888 } 16889 $this->x = $tmpx; 16890 $this->lispacer = ''; 16891 } 16892 16893 /** 16894 * Returns current graphic variables as array. 16895 * @return array graphic variables 16896 * @access protected 16897 * @since 4.2.010 (2008-11-14) 16898 */ 16899 protected function getGraphicVars() { 16900 $grapvars = array( 16901 'FontFamily' => $this->FontFamily, 16902 'FontStyle' => $this->FontStyle, 16903 'FontSizePt' => $this->FontSizePt, 16904 'rMargin' => $this->rMargin, 16905 'lMargin' => $this->lMargin, 16906 'cMargin' => $this->cMargin, 16907 'LineWidth' => $this->LineWidth, 16908 'linestyleWidth' => $this->linestyleWidth, 16909 'linestyleCap' => $this->linestyleCap, 16910 'linestyleJoin' => $this->linestyleJoin, 16911 'linestyleDash' => $this->linestyleDash, 16912 'textrendermode' => $this->textrendermode, 16913 'textstrokewidth' => $this->textstrokewidth, 16914 'DrawColor' => $this->DrawColor, 16915 'FillColor' => $this->FillColor, 16916 'TextColor' => $this->TextColor, 16917 'ColorFlag' => $this->ColorFlag, 16918 'bgcolor' => $this->bgcolor, 16919 'fgcolor' => $this->fgcolor, 16920 'htmlvspace' => $this->htmlvspace, 16921 'lasth' => $this->lasth 16922 ); 16923 return $grapvars; 16924 } 16925 16926 /** 16927 * Set graphic variables. 16928 * @param $gvars array graphic variables 16929 * @access protected 16930 * @since 4.2.010 (2008-11-14) 16931 */ 16932 protected function setGraphicVars($gvars) { 16933 $this->FontFamily = $gvars['FontFamily']; 16934 $this->FontStyle = $gvars['FontStyle']; 16935 $this->FontSizePt = $gvars['FontSizePt']; 16936 $this->rMargin = $gvars['rMargin']; 16937 $this->lMargin = $gvars['lMargin']; 16938 $this->cMargin = $gvars['cMargin']; 16939 $this->LineWidth = $gvars['LineWidth']; 16940 $this->linestyleWidth = $gvars['linestyleWidth']; 16941 $this->linestyleCap = $gvars['linestyleCap']; 16942 $this->linestyleJoin = $gvars['linestyleJoin']; 16943 $this->linestyleDash = $gvars['linestyleDash']; 16944 $this->textrendermode = $gvars['textrendermode']; 16945 $this->textstrokewidth = $gvars['textstrokewidth']; 16946 $this->DrawColor = $gvars['DrawColor']; 16947 $this->FillColor = $gvars['FillColor']; 16948 $this->TextColor = $gvars['TextColor']; 16949 $this->ColorFlag = $gvars['ColorFlag']; 16950 $this->bgcolor = $gvars['bgcolor']; 16951 $this->fgcolor = $gvars['fgcolor']; 16952 $this->htmlvspace = $gvars['htmlvspace']; 16953 //$this->lasth = $gvars['lasth']; 16954 $this->_out(''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor.''); 16955 if (!$this->empty_string($this->FontFamily)) { 16956 $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt); 16957 } 16958 } 16959 16960 /** 16961 * Returns a temporary filename for caching object on filesystem. 16962 * @param string $prefix prefix to add to filename 16963 * return string filename. 16964 * @access protected 16965 * @since 4.5.000 (2008-12-31) 16966 */ 16967 protected function getObjFilename($name) { 16968 return tempnam(K_PATH_CACHE, $name.'_'); 16969 } 16970 16971 /** 16972 * Writes data to a temporary file on filesystem. 16973 * @param string $file file name 16974 * @param mixed $data data to write on file 16975 * @param boolean $append if true append data, false replace. 16976 * @access protected 16977 * @since 4.5.000 (2008-12-31) 16978 */ 16979 protected function writeDiskCache($filename, $data, $append=false) { 16980 if ($append) { 16981 $fmode = 'ab+'; 16982 } else { 16983 $fmode = 'wb+'; 16984 } 16985 $f = @fopen($filename, $fmode); 16986 if (!$f) { 16987 $this->Error('Unable to write cache file: '.$filename); 16988 } else { 16989 fwrite($f, $data); 16990 fclose($f); 16991 } 16992 // update file length (needed for transactions) 16993 if (!isset($this->cache_file_length['_'.$filename])) { 16994 $this->cache_file_length['_'.$filename] = strlen($data); 16995 } else { 16996 $this->cache_file_length['_'.$filename] += strlen($data); 16997 } 16998 } 16999 17000 /** 17001 * Read data from a temporary file on filesystem. 17002 * @param string $file file name 17003 * @return mixed retrieved data 17004 * @access protected 17005 * @since 4.5.000 (2008-12-31) 17006 */ 17007 protected function readDiskCache($filename) { 17008 return file_get_contents($filename); 17009 } 17010 17011 /** 17012 * Set buffer content (always append data). 17013 * @param string $data data 17014 * @access protected 17015 * @since 4.5.000 (2009-01-02) 17016 */ 17017 protected function setBuffer($data) { 17018 $this->bufferlen += strlen($data); 17019 if ($this->diskcache) { 17020 if (!isset($this->buffer) OR $this->empty_string($this->buffer)) { 17021 $this->buffer = $this->getObjFilename('buffer'); 17022 } 17023 $this->writeDiskCache($this->buffer, $data, true); 17024 } else { 17025 $this->buffer .= $data; 17026 } 17027 } 17028 17029 /** 17030 * Get buffer content. 17031 * @return string buffer content 17032 * @access protected 17033 * @since 4.5.000 (2009-01-02) 17034 */ 17035 protected function getBuffer() { 17036 if ($this->diskcache) { 17037 return $this->readDiskCache($this->buffer); 17038 } else { 17039 return $this->buffer; 17040 } 17041 } 17042 17043 /** 17044 * Set page buffer content. 17045 * @param int $page page number 17046 * @param string $data page data 17047 * @param boolean $append if true append data, false replace. 17048 * @access protected 17049 * @since 4.5.000 (2008-12-31) 17050 */ 17051 protected function setPageBuffer($page, $data, $append=false) { 17052 if ($this->diskcache) { 17053 if (!isset($this->pages[$page])) { 17054 $this->pages[$page] = $this->getObjFilename('page'.$page); 17055 } 17056 $this->writeDiskCache($this->pages[$page], $data, $append); 17057 } else { 17058 if ($append) { 17059 $this->pages[$page] .= $data; 17060 } else { 17061 $this->pages[$page] = $data; 17062 } 17063 } 17064 if ($append AND isset($this->pagelen[$page])) { 17065 $this->pagelen[$page] += strlen($data); 17066 } else { 17067 $this->pagelen[$page] = strlen($data); 17068 } 17069 } 17070 17071 /** 17072 * Get page buffer content. 17073 * @param int $page page number 17074 * @return string page buffer content or false in case of error 17075 * @access protected 17076 * @since 4.5.000 (2008-12-31) 17077 */ 17078 protected function getPageBuffer($page) { 17079 if ($this->diskcache) { 17080 return $this->readDiskCache($this->pages[$page]); 17081 } elseif (isset($this->pages[$page])) { 17082 return $this->pages[$page]; 17083 } 17084 return false; 17085 } 17086 17087 /** 17088 * Set image buffer content. 17089 * @param string $image image key 17090 * @param array $data image data 17091 * @access protected 17092 * @since 4.5.000 (2008-12-31) 17093 */ 17094 protected function setImageBuffer($image, $data) { 17095 if ($this->diskcache) { 17096 if (!isset($this->images[$image])) { 17097 $this->images[$image] = $this->getObjFilename('image'.$image); 17098 } 17099 $this->writeDiskCache($this->images[$image], serialize($data)); 17100 } else { 17101 $this->images[$image] = $data; 17102 } 17103 if (!in_array($image, $this->imagekeys)) { 17104 $this->imagekeys[] = $image; 17105 ++$this->numimages; 17106 } 17107 } 17108 17109 /** 17110 * Set image buffer content for a specified sub-key. 17111 * @param string $image image key 17112 * @param string $key image sub-key 17113 * @param array $data image data 17114 * @access protected 17115 * @since 4.5.000 (2008-12-31) 17116 */ 17117 protected function setImageSubBuffer($image, $key, $data) { 17118 if (!isset($this->images[$image])) { 17119 $this->setImageBuffer($image, array()); 17120 } 17121 if ($this->diskcache) { 17122 $tmpimg = $this->getImageBuffer($image); 17123 $tmpimg[$key] = $data; 17124 $this->writeDiskCache($this->images[$image], serialize($tmpimg)); 17125 } else { 17126 $this->images[$image][$key] = $data; 17127 } 17128 } 17129 17130 /** 17131 * Get image buffer content. 17132 * @param string $image image key 17133 * @return string image buffer content or false in case of error 17134 * @access protected 17135 * @since 4.5.000 (2008-12-31) 17136 */ 17137 protected function getImageBuffer($image) { 17138 if ($this->diskcache AND isset($this->images[$image])) { 17139 return unserialize($this->readDiskCache($this->images[$image])); 17140 } elseif (isset($this->images[$image])) { 17141 return $this->images[$image]; 17142 } 17143 return false; 17144 } 17145 17146 /** 17147 * Set font buffer content. 17148 * @param string $font font key 17149 * @param array $data font data 17150 * @access protected 17151 * @since 4.5.000 (2009-01-02) 17152 */ 17153 protected function setFontBuffer($font, $data) { 17154 if ($this->diskcache) { 17155 if (!isset($this->fonts[$font])) { 17156 $this->fonts[$font] = $this->getObjFilename('font'); 17157 } 17158 $this->writeDiskCache($this->fonts[$font], serialize($data)); 17159 } else { 17160 $this->fonts[$font] = $data; 17161 } 17162 if (!in_array($font, $this->fontkeys)) { 17163 $this->fontkeys[] = $font; 17164 } 17165 } 17166 17167 /** 17168 * Set font buffer content. 17169 * @param string $font font key 17170 * @param string $key font sub-key 17171 * @param array $data font data 17172 * @access protected 17173 * @since 4.5.000 (2009-01-02) 17174 */ 17175 protected function setFontSubBuffer($font, $key, $data) { 17176 if (!isset($this->fonts[$font])) { 17177 $this->setFontBuffer($font, array()); 17178 } 17179 if ($this->diskcache) { 17180 $tmpfont = $this->getFontBuffer($font); 17181 $tmpfont[$key] = $data; 17182 $this->writeDiskCache($this->fonts[$font], serialize($tmpfont)); 17183 } else { 17184 $this->fonts[$font][$key] = $data; 17185 } 17186 } 17187 17188 /** 17189 * Get font buffer content. 17190 * @param string $font font key 17191 * @return string font buffer content or false in case of error 17192 * @access protected 17193 * @since 4.5.000 (2009-01-02) 17194 */ 17195 protected function getFontBuffer($font) { 17196 if ($this->diskcache AND isset($this->fonts[$font])) { 17197 return unserialize($this->readDiskCache($this->fonts[$font])); 17198 } elseif (isset($this->fonts[$font])) { 17199 return $this->fonts[$font]; 17200 } 17201 return false; 17202 } 17203 17204 /** 17205 * Move a page to a previous position. 17206 * @param int $frompage number of the source page 17207 * @param int $topage number of the destination page (must be less than $frompage) 17208 * @return true in case of success, false in case of error. 17209 * @access public 17210 * @since 4.5.000 (2009-01-02) 17211 */ 17212 public function movePage($frompage, $topage) { 17213 if (($frompage > $this->numpages) OR ($frompage <= $topage)) { 17214 return false; 17215 } 17216 if ($frompage == $this->page) { 17217 // close the page before moving it 17218 $this->endPage(); 17219 } 17220 // move all page-related states 17221 $tmppage = $this->pages[$frompage]; 17222 $tmppagedim = $this->pagedim[$frompage]; 17223 $tmppagelen = $this->pagelen[$frompage]; 17224 $tmpintmrk = $this->intmrk[$frompage]; 17225 if (isset($this->footerpos[$frompage])) { 17226 $tmpfooterpos = $this->footerpos[$frompage]; 17227 } 17228 if (isset($this->footerlen[$frompage])) { 17229 $tmpfooterlen = $this->footerlen[$frompage]; 17230 } 17231 if (isset($this->transfmrk[$frompage])) { 17232 $tmptransfmrk = $this->transfmrk[$frompage]; 17233 } 17234 if (isset($this->PageAnnots[$frompage])) { 17235 $tmpannots = $this->PageAnnots[$frompage]; 17236 } 17237 if (isset($this->newpagegroup[$frompage])) { 17238 $tmpnewpagegroup = $this->newpagegroup[$frompage]; 17239 } 17240 for ($i = $frompage; $i > $topage; --$i) { 17241 $j = $i - 1; 17242 // shift pages down 17243 $this->pages[$i] = $this->pages[$j]; 17244 $this->pagedim[$i] = $this->pagedim[$j]; 17245 $this->pagelen[$i] = $this->pagelen[$j]; 17246 $this->intmrk[$i] = $this->intmrk[$j]; 17247 if (isset($this->footerpos[$j])) { 17248 $this->footerpos[$i] = $this->footerpos[$j]; 17249 } elseif (isset($this->footerpos[$i])) { 17250 unset($this->footerpos[$i]); 17251 } 17252 if (isset($this->footerlen[$j])) { 17253 $this->footerlen[$i] = $this->footerlen[$j]; 17254 } elseif (isset($this->footerlen[$i])) { 17255 unset($this->footerlen[$i]); 17256 } 17257 if (isset($this->transfmrk[$j])) { 17258 $this->transfmrk[$i] = $this->transfmrk[$j]; 17259 } elseif (isset($this->transfmrk[$i])) { 17260 unset($this->transfmrk[$i]); 17261 } 17262 if (isset($this->PageAnnots[$j])) { 17263 $this->PageAnnots[$i] = $this->PageAnnots[$j]; 17264 } elseif (isset($this->PageAnnots[$i])) { 17265 unset($this->PageAnnots[$i]); 17266 } 17267 if (isset($this->newpagegroup[$j])) { 17268 $this->newpagegroup[$i] = $this->newpagegroup[$j]; 17269 } elseif (isset($this->newpagegroup[$i])) { 17270 unset($this->newpagegroup[$i]); 17271 } 17272 } 17273 $this->pages[$topage] = $tmppage; 17274 $this->pagedim[$topage] = $tmppagedim; 17275 $this->pagelen[$topage] = $tmppagelen; 17276 $this->intmrk[$topage] = $tmpintmrk; 17277 if (isset($tmpfooterpos)) { 17278 $this->footerpos[$topage] = $tmpfooterpos; 17279 } elseif (isset($this->footerpos[$topage])) { 17280 unset($this->footerpos[$topage]); 17281 } 17282 if (isset($tmpfooterlen)) { 17283 $this->footerlen[$topage] = $tmpfooterlen; 17284 } elseif (isset($this->footerlen[$topage])) { 17285 unset($this->footerlen[$topage]); 17286 } 17287 if (isset($tmptransfmrk)) { 17288 $this->transfmrk[$topage] = $tmptransfmrk; 17289 } elseif (isset($this->transfmrk[$topage])) { 17290 unset($this->transfmrk[$topage]); 17291 } 17292 if (isset($tmpannots)) { 17293 $this->PageAnnots[$topage] = $tmpannots; 17294 } elseif (isset($this->PageAnnots[$topage])) { 17295 unset($this->PageAnnots[$topage]); 17296 } 17297 if (isset($tmpnewpagegroup)) { 17298 $this->newpagegroup[$topage] = $tmpnewpagegroup; 17299 } elseif (isset($this->newpagegroup[$topage])) { 17300 unset($this->newpagegroup[$topage]); 17301 } 17302 // adjust outlines 17303 $tmpoutlines = $this->outlines; 17304 foreach ($tmpoutlines as $key => $outline) { 17305 if (($outline['p'] >= $topage) AND ($outline['p'] < $frompage)) { 17306 $this->outlines[$key]['p'] = $outline['p'] + 1; 17307 } elseif ($outline['p'] == $frompage) { 17308 $this->outlines[$key]['p'] = $topage; 17309 } 17310 } 17311 // adjust links 17312 $tmplinks = $this->links; 17313 foreach ($tmplinks as $key => $link) { 17314 if (($link[0] >= $topage) AND ($link[0] < $frompage)) { 17315 $this->links[$key][0] = $link[0] + 1; 17316 } elseif ($link[0] == $frompage) { 17317 $this->links[$key][0] = $topage; 17318 } 17319 } 17320 // adjust javascript 17321 $tmpjavascript = $this->javascript; 17322 global $jfrompage, $jtopage; 17323 $jfrompage = $frompage; 17324 $jtopage = $topage; 17325 $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', 17326 create_function('$matches', 'global $jfrompage, $jtopage; 17327 $pagenum = intval($matches[3]) + 1; 17328 if (($pagenum >= $jtopage) AND ($pagenum < $jfrompage)) { 17329 $newpage = ($pagenum + 1); 17330 } elseif ($pagenum == $jfrompage) { 17331 $newpage = $jtopage; 17332 } else { 17333 $newpage = $pagenum; 17334 } 17335 --$newpage; 17336 return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript); 17337 // return to last page 17338 $this->lastPage(true); 17339 return true; 17340 } 17341 17342 /** 17343 * Remove the specified page. 17344 * @param int $page page to remove 17345 * @return true in case of success, false in case of error. 17346 * @access public 17347 * @since 4.6.004 (2009-04-23) 17348 */ 17349 public function deletePage($page) { 17350 if ($page > $this->numpages) { 17351 return false; 17352 } 17353 // delete current page 17354 unset($this->pages[$page]); 17355 unset($this->pagedim[$page]); 17356 unset($this->pagelen[$page]); 17357 unset($this->intmrk[$page]); 17358 if (isset($this->footerpos[$page])) { 17359 unset($this->footerpos[$page]); 17360 } 17361 if (isset($this->footerlen[$page])) { 17362 unset($this->footerlen[$page]); 17363 } 17364 if (isset($this->transfmrk[$page])) { 17365 unset($this->transfmrk[$page]); 17366 } 17367 if (isset($this->PageAnnots[$page])) { 17368 unset($this->PageAnnots[$page]); 17369 } 17370 if (isset($this->newpagegroup[$page])) { 17371 unset($this->newpagegroup[$page]); 17372 } 17373 if (isset($this->pageopen[$page])) { 17374 unset($this->pageopen[$page]); 17375 } 17376 // update remaining pages 17377 for ($i = $page; $i < $this->numpages; ++$i) { 17378 $j = $i + 1; 17379 // shift pages 17380 $this->pages[$i] = $this->pages[$j]; 17381 $this->pagedim[$i] = $this->pagedim[$j]; 17382 $this->pagelen[$i] = $this->pagelen[$j]; 17383 $this->intmrk[$i] = $this->intmrk[$j]; 17384 if (isset($this->footerpos[$j])) { 17385 $this->footerpos[$i] = $this->footerpos[$j]; 17386 } elseif (isset($this->footerpos[$i])) { 17387 unset($this->footerpos[$i]); 17388 } 17389 if (isset($this->footerlen[$j])) { 17390 $this->footerlen[$i] = $this->footerlen[$j]; 17391 } elseif (isset($this->footerlen[$i])) { 17392 unset($this->footerlen[$i]); 17393 } 17394 if (isset($this->transfmrk[$j])) { 17395 $this->transfmrk[$i] = $this->transfmrk[$j]; 17396 } elseif (isset($this->transfmrk[$i])) { 17397 unset($this->transfmrk[$i]); 17398 } 17399 if (isset($this->PageAnnots[$j])) { 17400 $this->PageAnnots[$i] = $this->PageAnnots[$j]; 17401 } elseif (isset($this->PageAnnots[$i])) { 17402 unset($this->PageAnnots[$i]); 17403 } 17404 if (isset($this->newpagegroup[$j])) { 17405 $this->newpagegroup[$i] = $this->newpagegroup[$j]; 17406 } elseif (isset($this->newpagegroup[$i])) { 17407 unset($this->newpagegroup[$i]); 17408 } 17409 if (isset($this->pageopen[$j])) { 17410 $this->pageopen[$i] = $this->pageopen[$j]; 17411 } elseif (isset($this->pageopen[$i])) { 17412 unset($this->pageopen[$i]); 17413 } 17414 } 17415 // remove last page 17416 unset($this->pages[$this->numpages]); 17417 unset($this->pagedim[$this->numpages]); 17418 unset($this->pagelen[$this->numpages]); 17419 unset($this->intmrk[$this->numpages]); 17420 if (isset($this->footerpos[$this->numpages])) { 17421 unset($this->footerpos[$this->numpages]); 17422 } 17423 if (isset($this->footerlen[$this->numpages])) { 17424 unset($this->footerlen[$this->numpages]); 17425 } 17426 if (isset($this->transfmrk[$this->numpages])) { 17427 unset($this->transfmrk[$this->numpages]); 17428 } 17429 if (isset($this->PageAnnots[$this->numpages])) { 17430 unset($this->PageAnnots[$this->numpages]); 17431 } 17432 if (isset($this->newpagegroup[$this->numpages])) { 17433 unset($this->newpagegroup[$this->numpages]); 17434 } 17435 if (isset($this->pageopen[$this->numpages])) { 17436 unset($this->pageopen[$this->numpages]); 17437 } 17438 --$this->numpages; 17439 $this->page = $this->numpages; 17440 // adjust outlines 17441 $tmpoutlines = $this->outlines; 17442 foreach ($tmpoutlines as $key => $outline) { 17443 if ($outline['p'] > $page) { 17444 $this->outlines[$key]['p'] = $outline['p'] - 1; 17445 } elseif ($outline['p'] == $page) { 17446 unset($this->outlines[$key]); 17447 } 17448 } 17449 // adjust links 17450 $tmplinks = $this->links; 17451 foreach ($tmplinks as $key => $link) { 17452 if ($link[0] > $page) { 17453 $this->links[$key][0] = $link[0] - 1; 17454 } elseif ($link[0] == $page) { 17455 unset($this->links[$key]); 17456 } 17457 } 17458 // adjust javascript 17459 $tmpjavascript = $this->javascript; 17460 global $jpage; 17461 $jpage = $page; 17462 $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', 17463 create_function('$matches', 'global $jpage; 17464 $pagenum = intval($matches[3]) + 1; 17465 if ($pagenum >= $jpage) { 17466 $newpage = ($pagenum - 1); 17467 } elseif ($pagenum == $jpage) { 17468 $newpage = 1; 17469 } else { 17470 $newpage = $pagenum; 17471 } 17472 --$newpage; 17473 return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript); 17474 // return to last page 17475 $this->lastPage(true); 17476 return true; 17477 } 17478 17479 /** 17480 * Clone the specified page to a new page. 17481 * @param int $page number of page to copy (0 = current page) 17482 * @return true in case of success, false in case of error. 17483 * @access public 17484 * @since 4.9.015 (2010-04-20) 17485 */ 17486 public function copyPage($page=0) { 17487 if ($page == 0) { 17488 // default value 17489 $page = $this->page; 17490 } 17491 if ($page > $this->numpages) { 17492 return false; 17493 } 17494 if ($page == $this->page) { 17495 // close the page before cloning it 17496 $this->endPage(); 17497 } 17498 // copy all page-related states 17499 ++$this->numpages; 17500 $this->page = $this->numpages; 17501 $this->pages[$this->page] = $this->pages[$page]; 17502 $this->pagedim[$this->page] = $this->pagedim[$page]; 17503 $this->pagelen[$this->page] = $this->pagelen[$page]; 17504 $this->intmrk[$this->page] = $this->intmrk[$page]; 17505 $this->pageopen[$this->page] = false; 17506 if (isset($this->footerpos[$page])) { 17507 $this->footerpos[$this->page] = $this->footerpos[$page]; 17508 } 17509 if (isset($this->footerlen[$page])) { 17510 $this->footerlen[$this->page] = $this->footerlen[$page]; 17511 } 17512 if (isset($this->transfmrk[$page])) { 17513 $this->transfmrk[$this->page] = $this->transfmrk[$page]; 17514 } 17515 if (isset($this->PageAnnots[$page])) { 17516 $this->PageAnnots[$this->page] = $this->PageAnnots[$page]; 17517 } 17518 if (isset($this->newpagegroup[$page])) { 17519 $this->newpagegroup[$this->page] = $this->newpagegroup[$page]; 17520 } 17521 // copy outlines 17522 $tmpoutlines = $this->outlines; 17523 foreach ($tmpoutlines as $key => $outline) { 17524 if ($outline['p'] == $page) { 17525 $this->outlines[] = array('t' => $outline['t'], 'l' => $outline['l'], 'y' => $outline['y'], 'p' => $this->page); 17526 } 17527 } 17528 // copy links 17529 $tmplinks = $this->links; 17530 foreach ($tmplinks as $key => $link) { 17531 if ($link[0] == $page) { 17532 $this->links[] = array($this->page, $link[1]); 17533 } 17534 } 17535 // return to last page 17536 $this->lastPage(true); 17537 return true; 17538 } 17539 17540 /** 17541 * Output a Table of Content Index (TOC). 17542 * Before calling this method you have to open the page using the addTOCPage() method. 17543 * After calling this method you have to call endTOCPage() to close the TOC page. 17544 * You can override this method to achieve different styles. 17545 * @param int $page page number where this TOC should be inserted (leave empty for current page). 17546 * @param string $numbersfont set the font for page numbers (please use monospaced font for better alignment). 17547 * @param string $filler string used to fill the space between text and page number. 17548 * @param string $toc_name name to use for TOC bookmark. 17549 * @access public 17550 * @author Nicola Asuni 17551 * @since 4.5.000 (2009-01-02) 17552 * @see addTOCPage(), endTOCPage(), addHTMLTOC() 17553 */ 17554 public function addTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC') { 17555 $fontsize = $this->FontSizePt; 17556 $fontfamily = $this->FontFamily; 17557 $fontstyle = $this->FontStyle; 17558 $w = $this->w - $this->lMargin - $this->rMargin; 17559 $spacer = $this->GetStringWidth(chr(32)) * 4; 17560 $page_first = $this->getPage(); 17561 $lmargin = $this->lMargin; 17562 $rmargin = $this->rMargin; 17563 $x_start = $this->GetX(); 17564 if ($this->empty_string($numbersfont)) { 17565 $numbersfont = $this->default_monospaced_font; 17566 } 17567 if ($this->empty_string($filler)) { 17568 $filler = ' '; 17569 } 17570 if ($this->empty_string($page)) { 17571 $gap = ' '; 17572 } else { 17573 $gap = ''; 17574 } 17575 foreach ($this->outlines as $key => $outline) { 17576 if ($this->rtl) { 17577 $aligntext = 'R'; 17578 $alignnum = 'L'; 17579 } else { 17580 $aligntext = 'L'; 17581 $alignnum = 'R'; 17582 } 17583 if ($outline['l'] == 0) { 17584 $this->SetFont($fontfamily, $fontstyle.'B', $fontsize); 17585 } else { 17586 $this->SetFont($fontfamily, $fontstyle, $fontsize - $outline['l']); 17587 } 17588 $indent = ($spacer * $outline['l']); 17589 if ($this->rtl) { 17590 $this->rMargin += $indent; 17591 $this->x -= $indent; 17592 } else { 17593 $this->lMargin += $indent; 17594 $this->x += $indent; 17595 } 17596 $link = $this->AddLink(); 17597 $this->SetLink($link, 0, $outline['p']); 17598 // write the text 17599 $this->Write(0, $outline['t'], $link, 0, $aligntext, false, 0, false, false, 0); 17600 $this->SetFont($numbersfont, $fontstyle, $fontsize); 17601 if ($this->empty_string($page)) { 17602 $pagenum = $outline['p']; 17603 } else { 17604 // placemark to be replaced with the correct number 17605 $pagenum = '{#'.($outline['p']).'}'; 17606 if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) { 17607 $pagenum = '{'.$pagenum.'}'; 17608 } 17609 } 17610 $numwidth = $this->GetStringWidth($pagenum); 17611 if ($this->rtl) { 17612 $tw = $this->x - $this->lMargin; 17613 } else { 17614 $tw = $this->w - $this->rMargin - $this->x; 17615 } 17616 $fw = $tw - $numwidth - $this->GetStringWidth(chr(32)); 17617 $numfills = floor($fw / $this->GetStringWidth($filler)); 17618 if ($numfills > 0) { 17619 $rowfill = str_repeat($filler, $numfills); 17620 } else { 17621 $rowfill = ''; 17622 } 17623 if ($this->rtl) { 17624 $pagenum = $pagenum.$gap.$rowfill.' '; 17625 } else { 17626 $pagenum = ' '.$rowfill.$gap.$pagenum; 17627 } 17628 // write the number 17629 $this->Cell($tw, 0, $pagenum, 0, 1, $alignnum, 0, $link, 0); 17630 $this->SetX($x_start); 17631 $this->lMargin = $lmargin; 17632 $this->rMargin = $rmargin; 17633 } 17634 $page_last = $this->getPage(); 17635 $numpages = $page_last - $page_first + 1; 17636 if (!$this->empty_string($page)) { 17637 for ($p = $page_first; $p <= $page_last; ++$p) { 17638 // get page data 17639 $temppage = $this->getPageBuffer($p); 17640 for ($n = 1; $n <= $this->numpages; ++$n) { 17641 // update page numbers 17642 $k = '{#'.$n.'}'; 17643 $ku = '{'.$k.'}'; 17644 $alias_a = $this->_escape($k); 17645 $alias_au = $this->_escape('{'.$k.'}'); 17646 if ($this->isunicode) { 17647 $alias_b = $this->_escape($this->UTF8ToLatin1($k)); 17648 $alias_bu = $this->_escape($this->UTF8ToLatin1($ku)); 17649 $alias_c = $this->_escape($this->utf8StrRev($k, false, $this->tmprtl)); 17650 $alias_cu = $this->_escape($this->utf8StrRev($ku, false, $this->tmprtl)); 17651 } 17652 if ($n >= $page) { 17653 $np = $n + $numpages; 17654 } else { 17655 $np = $n; 17656 } 17657 $ns = $this->formatTOCPageNumber($np); 17658 $nu = $ns; 17659 $sdiff = strlen($k) - strlen($ns) - 1; 17660 $sdiffu = strlen($ku) - strlen($ns) - 1; 17661 $sfill = str_repeat($filler, $sdiff); 17662 $sfillu = str_repeat($filler, $sdiffu); 17663 if ($this->rtl) { 17664 $ns = $ns.' '.$sfill; 17665 $nu = $nu.' '.$sfillu; 17666 } else { 17667 $ns = $sfill.' '.$ns; 17668 $nu = $sfillu.' '.$nu; 17669 } 17670 $nu = $this->UTF8ToUTF16BE($nu, false); 17671 $temppage = str_replace($alias_au, $nu, $temppage); 17672 if ($this->isunicode) { 17673 $temppage = str_replace($alias_bu, $nu, $temppage); 17674 $temppage = str_replace($alias_cu, $nu, $temppage); 17675 $temppage = str_replace($alias_b, $ns, $temppage); 17676 $temppage = str_replace($alias_c, $ns, $temppage); 17677 } 17678 $temppage = str_replace($alias_a, $ns, $temppage); 17679 } 17680 // save changes 17681 $this->setPageBuffer($p, $temppage); 17682 } 17683 // move pages 17684 $this->Bookmark($toc_name, 0, 0, $page_first); 17685 for ($i = 0; $i < $numpages; ++$i) { 17686 $this->movePage($page_last, $page); 17687 } 17688 } 17689 } 17690 17691 /** 17692 * Output a Table Of Content Index (TOC) using HTML templates. 17693 * Before calling this method you have to open the page using the addTOCPage() method. 17694 * After calling this method you have to call endTOCPage() to close the TOC page. 17695 * @param int $page page number where this TOC should be inserted (leave empty for current page). 17696 * @param string $toc_name name to use for TOC bookmark. 17697 * @param array $templates array of html templates. Use: #TOC_DESCRIPTION# for bookmark title, #TOC_PAGE_NUMBER# for page number. 17698 * @parma boolean $correct_align if true correct the number alignment (numbers must be in monospaced font like courier and right aligned on LTR, or left aligned on RTL) 17699 * @access public 17700 * @author Nicola Asuni 17701 * @since 5.0.001 (2010-05-06) 17702 * @see addTOCPage(), endTOCPage(), addTOC() 17703 */ 17704 public function addHTMLTOC($page='', $toc_name='TOC', $templates=array(), $correct_align=true) { 17705 $prev_htmlLinkColorArray = $this->htmlLinkColorArray; 17706 $prev_htmlLinkFontStyle = $this->htmlLinkFontStyle; 17707 // set new style for link 17708 $this->htmlLinkColorArray = array(); 17709 $this->htmlLinkFontStyle = ''; 17710 $page_first = $this->getPage(); 17711 foreach ($this->outlines as $key => $outline) { 17712 if ($this->empty_string($page)) { 17713 $pagenum = $outline['p']; 17714 } else { 17715 // placemark to be replaced with the correct number 17716 $pagenum = '{#'.($outline['p']).'}'; 17717 if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) { 17718 $pagenum = '{'.$pagenum.'}'; 17719 } 17720 } 17721 // get HTML template 17722 $row = $templates[$outline['l']]; 17723 // replace templates with current values 17724 $row = str_replace('#TOC_DESCRIPTION#', $outline['t'], $row); 17725 $row = str_replace('#TOC_PAGE_NUMBER#', $pagenum, $row); 17726 // add link to page 17727 $row = '<a href="#'.$outline['p'].'">'.$row.'</a>'; 17728 // write bookmark entry 17729 $this->writeHTML($row, false, false, true, false, ''); 17730 } 17731 // restore link styles 17732 $this->htmlLinkColorArray = $prev_htmlLinkColorArray; 17733 $this->htmlLinkFontStyle = $prev_htmlLinkFontStyle; 17734 // move TOC page and replace numbers 17735 $page_last = $this->getPage(); 17736 $numpages = $page_last - $page_first + 1; 17737 if (!$this->empty_string($page)) { 17738 for ($p = $page_first; $p <= $page_last; ++$p) { 17739 // get page data 17740 $temppage = $this->getPageBuffer($p); 17741 for ($n = 1; $n <= $this->numpages; ++$n) { 17742 // update page numbers 17743 $k = '{#'.$n.'}'; 17744 $ku = '{'.$k.'}'; 17745 $alias_a = $this->_escape($k); 17746 $alias_au = $this->_escape('{'.$k.'}'); 17747 if ($this->isunicode) { 17748 $alias_b = $this->_escape($this->UTF8ToLatin1($k)); 17749 $alias_bu = $this->_escape($this->UTF8ToLatin1($ku)); 17750 $alias_c = $this->_escape($this->utf8StrRev($k, false, $this->tmprtl)); 17751 $alias_cu = $this->_escape($this->utf8StrRev($ku, false, $this->tmprtl)); 17752 } 17753 if ($n >= $page) { 17754 $np = $n + $numpages; 17755 } else { 17756 $np = $n; 17757 } 17758 $ns = $this->formatTOCPageNumber($np); 17759 $nu = $ns; 17760 if ($correct_align) { 17761 $sdiff = strlen($k) - strlen($ns); 17762 $sdiffu = strlen($ku) - strlen($ns); 17763 $sfill = str_repeat(' ', $sdiff); 17764 $sfillu = str_repeat(' ', $sdiffu); 17765 if ($this->rtl) { 17766 $ns = $ns.$sfill; 17767 $nu = $nu.$sfillu; 17768 } else { 17769 $ns = $sfill.$ns; 17770 $nu = $sfillu.$nu; 17771 } 17772 } 17773 $nu = $this->UTF8ToUTF16BE($nu, false); 17774 $temppage = str_replace($alias_au, $nu, $temppage); 17775 if ($this->isunicode) { 17776 $temppage = str_replace($alias_bu, $nu, $temppage); 17777 $temppage = str_replace($alias_cu, $nu, $temppage); 17778 $temppage = str_replace($alias_b, $ns, $temppage); 17779 $temppage = str_replace($alias_c, $ns, $temppage); 17780 } 17781 $temppage = str_replace($alias_a, $ns, $temppage); 17782 } 17783 // save changes 17784 $this->setPageBuffer($p, $temppage); 17785 } 17786 // move pages 17787 $this->Bookmark($toc_name, 0, 0, $page_first); 17788 for ($i = 0; $i < $numpages; ++$i) { 17789 $this->movePage($page_last, $page); 17790 } 17791 } 17792 } 17793 17794 /** 17795 * Stores a copy of the current TCPDF object used for undo operation. 17796 * @access public 17797 * @since 4.5.029 (2009-03-19) 17798 */ 17799 public function startTransaction() { 17800 if (isset($this->objcopy)) { 17801 // remove previous copy 17802 $this->commitTransaction(); 17803 } 17804 // record current page number and Y position 17805 $this->start_transaction_page = $this->page; 17806 $this->start_transaction_y = $this->y; 17807 // clone current object 17808 $this->objcopy = $this->objclone($this); 17809 } 17810 17811 /** 17812 * Delete the copy of the current TCPDF object used for undo operation. 17813 * @access public 17814 * @since 4.5.029 (2009-03-19) 17815 */ 17816 public function commitTransaction() { 17817 if (isset($this->objcopy)) { 17818 $this->objcopy->_destroy(true, true); 17819 unset($this->objcopy); 17820 } 17821 } 17822 17823 /** 17824 * This method allows to undo the latest transaction by returning the latest saved TCPDF object with startTransaction(). 17825 * @param boolean $self if true restores current class object to previous state without the need of reassignment via the returned value. 17826 * @return TCPDF object. 17827 * @access public 17828 * @since 4.5.029 (2009-03-19) 17829 */ 17830 public function rollbackTransaction($self=false) { 17831 if (isset($this->objcopy)) { 17832 if (isset($this->objcopy->diskcache) AND $this->objcopy->diskcache) { 17833 // truncate files to previous values 17834 foreach ($this->objcopy->cache_file_length as $file => $length) { 17835 $file = substr($file, 1); 17836 $handle = fopen($file, 'r+'); 17837 ftruncate($handle, $length); 17838 } 17839 } 17840 $this->_destroy(true, true); 17841 if ($self) { 17842 $objvars = get_object_vars($this->objcopy); 17843 foreach ($objvars as $key => $value) { 17844 $this->$key = $value; 17845 } 17846 } 17847 return $this->objcopy; 17848 } 17849 return $this; 17850 } 17851 17852 /** 17853 * Creates a copy of a class object 17854 * @param object $object class object to be cloned 17855 * @return cloned object 17856 * @access public 17857 * @since 4.5.029 (2009-03-19) 17858 */ 17859 public function objclone($object) { 17860 return @clone($object); 17861 } 17862 17863 /** 17864 * Determine whether a string is empty. 17865 * @param string $str string to be checked 17866 * @return boolean true if string is empty 17867 * @access public 17868 * @since 4.5.044 (2009-04-16) 17869 */ 17870 public function empty_string($str) { 17871 return (is_null($str) OR (is_string($str) AND (strlen($str) == 0))); 17872 } 17873 17874 /** 17875 * Find position of last occurrence of a substring in a string 17876 * @param string $haystack The string to search in. 17877 * @param string $needle substring to search. 17878 * @param int $offset May be specified to begin searching an arbitrary number of characters into the string. 17879 * @return Returns the position where the needle exists. Returns FALSE if the needle was not found. 17880 * @access public 17881 * @since 4.8.038 (2010-03-13) 17882 */ 17883 public function revstrpos($haystack, $needle, $offset = 0) { 17884 $length = strlen($haystack); 17885 $offset = ($offset > 0)?($length - $offset):abs($offset); 17886 $pos = strpos(strrev($haystack), strrev($needle), $offset); 17887 return ($pos === false)?false:($length - $pos - strlen($needle)); 17888 } 17889 17890 // --- MULTI COLUMNS METHODS ----------------------- 17891 17892 /** 17893 * Set multiple columns of the same size 17894 * @param int $numcols number of columns (set to zero to disable columns mode) 17895 * @param int $width column width 17896 * @param int $y column starting Y position (leave empty for current Y position) 17897 * @access public 17898 * @since 4.9.001 (2010-03-28) 17899 */ 17900 public function setEqualColumns($numcols=0, $width=0, $y='') { 17901 $this->columns = array(); 17902 if ($numcols < 2) { 17903 $numcols = 0; 17904 $this->columns = array(); 17905 } else { 17906 // maximum column width 17907 $maxwidth = ($this->w - $this->original_lMargin - $this->original_rMargin) / $numcols; 17908 if ($width > $maxwidth) { 17909 $width = $maxwidth; 17910 } 17911 if ($this->empty_string($y)) { 17912 $y = $this->y; 17913 } 17914 // space between columns 17915 $space = (($this->w - $this->original_lMargin - $this->original_rMargin - ($numcols * $width)) / ($numcols - 1)); 17916 // fill the columns array (with, space, starting Y position) 17917 for ($i = 0; $i < $numcols; ++$i) { 17918 $this->columns[$i] = array('w' => $width, 's' => $space, 'y' => $y); 17919 } 17920 } 17921 $this->num_columns = $numcols; 17922 $this->current_column = 0; 17923 $this->column_start_page = $this->page; 17924 } 17925 17926 /** 17927 * Set columns array. 17928 * Each column is represented by and array with the following keys: (w = width, s = space between columns, y = column top position). 17929 * @param array $columns 17930 * @access public 17931 * @since 4.9.001 (2010-03-28) 17932 */ 17933 public function setColumnsArray($columns) { 17934 $this->columns = $columns; 17935 $this->num_columns = count($columns); 17936 $this->current_column = 0; 17937 $this->column_start_page = $this->page; 17938 } 17939 17940 /** 17941 * Set position at a given column 17942 * @param int $col column number (from 0 to getNumberOfColumns()-1); empty string = current column. 17943 * @access public 17944 * @since 4.9.001 (2010-03-28) 17945 */ 17946 public function selectColumn($col='') { 17947 if (is_string($col)) { 17948 $col = $this->current_column; 17949 } elseif($col >= $this->num_columns) { 17950 $col = 0; 17951 } 17952 if ($this->num_columns > 1) { 17953 if ($col != $this->current_column) { 17954 // move pointer at column top on the first page 17955 if ($this->column_start_page == $this->page) { 17956 $this->y = $this->columns[$col]['y']; 17957 } else { 17958 $this->y = $this->tMargin; 17959 } 17960 } 17961 // set X position of the current column by case 17962 $listindent = ($this->listindentlevel * $this->listindent); 17963 if ($this->rtl) { 17964 $x = $this->w - $this->original_rMargin - ($col * ($this->columns[$col]['w'] + $this->columns[$col]['s'])); 17965 $this->SetRightMargin($this->w - $x + $listindent); 17966 $this->SetLeftMargin($x - $this->columns[$col]['w']); 17967 $this->x = $x - $listindent; 17968 } else { 17969 $x = $this->original_lMargin + ($col * ($this->columns[$col]['w'] + $this->columns[$col]['s'])); 17970 $this->SetLeftMargin($x + $listindent); 17971 $this->SetRightMargin($this->w - $x - $this->columns[$col]['w']); 17972 $this->x = $x + $listindent; 17973 } 17974 $this->columns[$col]['x'] = $x; 17975 } 17976 $this->current_column = $col; 17977 // fix for HTML mode 17978 $this->newline = true; 17979 // print HTML table header (if any) 17980 if (!$this->empty_string($this->thead) AND (!$this->inthead)) { 17981 // print table header 17982 $this->writeHTML($this->thead, false, false, false, false, ''); 17983 } 17984 } 17985 17986 /** 17987 * Serialize an array of parameters to be used with TCPDF tag in HTML code. 17988 * @param array $pararray parameters array 17989 * @return sting containing serialized data 17990 * @access public 17991 * @since 4.9.006 (2010-04-02) 17992 */ 17993 public function serializeTCPDFtagParameters($pararray) { 17994 return urlencode(serialize($pararray)); 17995 } 17996 17997 /** 17998 * Set Text rendering mode. 17999 * @param int $stroke outline size in user units (0 = disable). 18000 * @param boolean $fill if true fills the text (default). 18001 * @param boolean $clip if true activate clipping mode 18002 * @access public 18003 * @since 4.9.008 (2009-04-02) 18004 */ 18005 public function setTextRenderingMode($stroke=0, $fill=true, $clip=false) { 18006 // Ref.: PDF 32000-1:2008 - 9.3.6 Text Rendering Mode 18007 // convert text rendering parameters 18008 if ($stroke < 0) { 18009 $stroke = 0; 18010 } 18011 if ($fill === true) { 18012 if ($stroke > 0) { 18013 if ($clip === true) { 18014 // Fill, then stroke text and add to path for clipping 18015 $textrendermode = 6; 18016 } else { 18017 // Fill, then stroke text 18018 $textrendermode = 2; 18019 } 18020 $textstrokewidth = $stroke; 18021 } else { 18022 if ($clip === true) { 18023 // Fill text and add to path for clipping 18024 $textrendermode = 4; 18025 } else { 18026 // Fill text 18027 $textrendermode = 0; 18028 } 18029 } 18030 } else { 18031 if ($stroke > 0) { 18032 if ($clip === true) { 18033 // Stroke text and add to path for clipping 18034 $textrendermode = 5; 18035 } else { 18036 // Stroke text 18037 $textrendermode = 1; 18038 } 18039 $textstrokewidth = $stroke; 18040 } else { 18041 if ($clip === true) { 18042 // Add text to path for clipping 18043 $textrendermode = 7; 18044 } else { 18045 // Neither fill nor stroke text (invisible) 18046 $textrendermode = 3; 18047 } 18048 } 18049 } 18050 $this->textrendermode = $textrendermode; 18051 $this->textstrokewidth = $stroke * $this->k; 18052 } 18053 18054 /** 18055 * Returns an array of chars containing soft hyphens. 18056 * @param array $word array of chars 18057 * @param array $patterns Array of hypenation patterns. 18058 * @param array $dictionary Array of words to be returned without applying the hyphenation algoritm. 18059 * @param int $leftmin Minimum number of character to leave on the left of the word without applying the hyphens. 18060 * @param int $rightmin Minimum number of character to leave on the right of the word without applying the hyphens. 18061 * @param int $charmin Minimum word lenght to apply the hyphenation algoritm. 18062 * @param int $charmax Maximum lenght of broken piece of word. 18063 * @return array text with soft hyphens 18064 * @author Nicola Asuni 18065 * @since 4.9.012 (2010-04-12) 18066 * @access protected 18067 */ 18068 protected function hyphenateWord($word, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) { 18069 $hyphenword = array(); // hyphens positions 18070 $numchars = count($word); 18071 if ($numchars <= $charmin) { 18072 return $word; 18073 } 18074 $word_string = $this->UTF8ArrSubString($word); 18075 // some words will be returned as-is 18076 $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})(\]?)$/'; 18077 if (preg_match($pattern, $word_string) > 0) { 18078 // email 18079 return $word; 18080 } 18081 $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})(\]?)$/'; 18082 if (preg_match($pattern, $word_string) > 0) { 18083 // URL 18084 return $word; 18085 } 18086 if (isset($dictionary[$word_string])) { 18087 return $this->UTF8StringToArray($dictionary[$word_string]); 18088 } 18089 // suround word with '_' characters 18090 $tmpword = array_merge(array(95), $word, array(95)); 18091 $tmpnumchars = $numchars + 2; 18092 $maxpos = $tmpnumchars - $charmin; 18093 for ($pos = 0; $pos < $maxpos; ++$pos) { 18094 $imax = min(($tmpnumchars - $pos), $charmax); 18095 for ($i = $charmin; $i <= $imax; ++$i) { 18096 $subword = strtolower($this->UTF8ArrSubString($tmpword, $pos, $pos + $i)); 18097 if (isset($patterns[$subword])) { 18098 $pattern = $this->UTF8StringToArray($patterns[$subword]); 18099 $pattern_length = count($pattern); 18100 $digits = 1; 18101 for ($j = 0; $j < $pattern_length; ++$j) { 18102 // check if $pattern[$j] is a number 18103 if (($pattern[$j] >= 48) AND ($pattern[$j] <= 57)) { 18104 if ($j == 0) { 18105 $zero = $pos - 1; 18106 } else { 18107 $zero = $pos + $j - $digits; 18108 } 18109 if (!isset($hyphenword[$zero]) OR ($hyphenword[$zero] != $pattern[$j])) { 18110 $hyphenword[$zero] = $this->unichr($pattern[$j]); 18111 } 18112 ++$digits; 18113 } 18114 } 18115 } 18116 } 18117 } 18118 $inserted = 0; 18119 $maxpos = $numchars - $rightmin; 18120 for($i = $leftmin; $i <= $maxpos; ++$i) { 18121 if(isset($hyphenword[$i]) AND (($hyphenword[$i] % 2) != 0)) { 18122 // 173 = soft hyphen character 18123 array_splice($word, $i + $inserted, 0, 173); 18124 ++$inserted; 18125 } 18126 } 18127 return $word; 18128 } 18129 18130 /** 18131 * Returns an array of hyphenation patterns. 18132 * @param string $file TEX file containing hypenation patterns. TEX pattrns can be downloaded from http://www.ctan.org/tex-archive/language/hyph-utf8/tex/generic/hyph-utf8/patterns/ 18133 * @return array of hyphenation patterns 18134 * @author Nicola Asuni 18135 * @since 4.9.012 (2010-04-12) 18136 * @access public 18137 */ 18138 public function getHyphenPatternsFromTEX($file) { 18139 // TEX patterns are available at: 18140 // http://www.ctan.org/tex-archive/language/hyph-utf8/tex/generic/hyph-utf8/patterns/ 18141 $data = file_get_contents($file); 18142 $patterns = array(); 18143 // remove comments 18144 $data = preg_replace('/\%[^\n]*/', '', $data); 18145 // extract the patterns part 18146 preg_match('/\\\\patterns\{([^\}]*)\}/i', $data, $matches); 18147 $data = trim(substr($matches[0], 10, -1)); 18148 // extract each pattern 18149 $patterns_array = preg_split('/[\s]+/', $data); 18150 // create new language array of patterns 18151 $patterns = array(); 18152 foreach($patterns_array as $val) { 18153 if (!$this->empty_string($val)) { 18154 $val = trim($val); 18155 $val = str_replace('\'', '\\\'', $val); 18156 $key = preg_replace('/[0-9]+/', '', $val); 18157 $patterns[$key] = $val; 18158 } 18159 } 18160 return $patterns; 18161 } 18162 18163 /** 18164 * Returns text with soft hyphens. 18165 * @param string $text text to process 18166 * @param mixed $patterns Array of hypenation patterns or a TEX file containing hypenation patterns. TEX patterns can be downloaded from http://www.ctan.org/tex-archive/language/hyph-utf8/tex/generic/hyph-utf8/patterns/ 18167 * @param array $dictionary Array of words to be returned without applying the hyphenation algoritm. 18168 * @param int $leftmin Minimum number of character to leave on the left of the word without applying the hyphens. 18169 * @param int $rightmin Minimum number of character to leave on the right of the word without applying the hyphens. 18170 * @param int $charmin Minimum word lenght to apply the hyphenation algoritm. 18171 * @param int $charmax Maximum lenght of broken piece of word. 18172 * @return array text with soft hyphens 18173 * @author Nicola Asuni 18174 * @since 4.9.012 (2010-04-12) 18175 * @access public 18176 */ 18177 public function hyphenateText($text, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) { 18178 global $unicode; 18179 $text = $this->unhtmlentities($text); 18180 $word = array(); // last word 18181 $txtarr = array(); // text to be returned 18182 $intag = false; // true if we are inside an HTML tag 18183 if (!is_array($patterns)) { 18184 $patterns = $this->getHyphenPatternsFromTEX($patterns); 18185 } 18186 // get array of characters 18187 $unichars = $this->UTF8StringToArray($text); 18188 // for each char 18189 foreach ($unichars as $char) { 18190 if ((!$intag) AND $unicode[$char] == 'L') { 18191 // letter character 18192 $word[] = $char; 18193 } else { 18194 // other type of character 18195 if (!$this->empty_string($word)) { 18196 // hypenate the word 18197 $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax)); 18198 $word = array(); 18199 } 18200 $txtarr[] = $char; 18201 if (chr($char) == '<') { 18202 // we are inside an HTML tag 18203 $intag = true; 18204 } elseif ($intag AND (chr($char) == '>')) { 18205 // end of HTML tag 18206 $intag = false; 18207 } 18208 } 18209 } 18210 if (!$this->empty_string($word)) { 18211 // hypenate the word 18212 $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax)); 18213 } 18214 // convert char array to string and return 18215 return $this->UTF8ArrSubString($txtarr); 18216 } 18217 18218 /** 18219 * Enable/disable rasterization of SVG images using ImageMagick library. 18220 * @param boolean $mode if true enable rasterization, false otherwise. 18221 * @access public 18222 * @since 5.0.000 (2010-04-27) 18223 */ 18224 public function setRasterizeVectorImages($mode) { 18225 $this->rasterize_vector_images = $mode; 18226 } 18227 18228 /** 18229 * Get the Path-Painting Operators. 18230 * @param string $style Style of rendering. Possible values are: 18231 * <ul> 18232 * <li>S or D: Stroke the path.</li> 18233 * <li>s or d: Close and stroke the path.</li> 18234 * <li>f or F: Fill the path, using the nonzero winding number rule to determine the region to fill.</li> 18235 * <li>f* or F*: Fill the path, using the even-odd rule to determine the region to fill.</li> 18236 * <li>B or FD or DF: Fill and then stroke the path, using the nonzero winding number rule to determine the region to fill.</li> 18237 * <li>B* or F*D or DF*: Fill and then stroke the path, using the even-odd rule to determine the region to fill.</li> 18238 * <li>b or fd or df: Close, fill, and then stroke the path, using the nonzero winding number rule to determine the region to fill.</li> 18239 * <li>b or f*d or df*: Close, fill, and then stroke the path, using the even-odd rule to determine the region to fill.</li> 18240 * <li>CNZ: Clipping mode using the even-odd rule to determine which regions lie inside the clipping path.</li> 18241 * <li>CEO: Clipping mode using the nonzero winding number rule to determine which regions lie inside the clipping path</li> 18242 * <li>n: End the path object without filling or stroking it.</li> 18243 * </ul> 18244 * @param string $default default style 18245 * @param boolean $mode if true enable rasterization, false otherwise. 18246 * @author Nicola Asuni 18247 * @access protected 18248 * @since 5.0.000 (2010-04-30) 18249 */ 18250 protected function getPathPaintOperator($style, $default='S') { 18251 $op = ''; 18252 switch($style) { 18253 case 'S': 18254 case 'D': { 18255 $op = 'S'; 18256 break; 18257 } 18258 case 's': 18259 case 'd': { 18260 $op = 's'; 18261 break; 18262 } 18263 case 'f': 18264 case 'F': { 18265 $op = 'f'; 18266 break; 18267 } 18268 case 'f*': 18269 case 'F*': { 18270 $op = 'f*'; 18271 break; 18272 } 18273 case 'B': 18274 case 'FD': 18275 case 'DF': { 18276 $op = 'B'; 18277 break; 18278 } 18279 case 'B*': 18280 case 'F*D': 18281 case 'DF*': { 18282 $op = 'B*'; 18283 break; 18284 } 18285 case 'b': 18286 case 'fd': 18287 case 'df': { 18288 $op = 'b'; 18289 break; 18290 } 18291 case 'b*': 18292 case 'f*d': 18293 case 'df*': { 18294 $op = 'b*'; 18295 break; 18296 } 18297 case 'CNZ': { 18298 $op = 'W n'; 18299 break; 18300 } 18301 case 'CEO': { 18302 $op = 'W* n'; 18303 break; 18304 } 18305 case 'n': { 18306 $op = 'n'; 18307 break; 18308 } 18309 default: { 18310 if (!empty($default)) { 18311 $op = $this->getPathPaintOperator($default, ''); 18312 } else { 18313 $op = ''; 18314 } 18315 } 18316 } 18317 return $op; 18318 } 18319 18320 // -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.- 18321 // SVG METHODS 18322 // -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.- 18323 18324 /** 18325 * Embedd a Scalable Vector Graphics (SVG) image. 18326 * NOTE: SVG standard is not yet fully implemented, use the setRasterizeVectorImages() method to enable/disable rasterization of SVG images using ImageMagick library. 18327 * @param string $file Name of the SVG file. 18328 * @param float $x Abscissa of the upper-left corner. 18329 * @param float $y Ordinate of the upper-left corner. 18330 * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated. 18331 * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated. 18332 * @param mixed $link URL or identifier returned by AddLink(). 18333 * @param string $align Indicates the alignment of the pointer next to image insertion relative to image height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul> 18334 * @param string $palign Allows to center or align the image on the current line. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul> 18335 * @param mixed $border Indicates if borders must be drawn around the image. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> 18336 * @param boolean $fitonpage if true the image is resized to not exceed page dimensions. 18337 * @author Nicola Asuni 18338 * @since 5.0.000 (2010-05-02) 18339 * @access public 18340 */ 18341 public function ImageSVG($file, $x='', $y='', $w=0, $h=0, $link='', $align='', $palign='', $border=0, $fitonpage=false) { 18342 if ($this->rasterize_vector_images) { 18343 // convert SVG to raster image using GD or ImageMagick libraries 18344 return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false); 18345 } 18346 $this->svgdir = dirname($file); 18347 $svgdata = file_get_contents($file); 18348 if ($svgdata === false) { 18349 $this->Error('SVG file not found: '.$file); 18350 } 18351 if ($x === '') { 18352 $x = $this->x; 18353 } 18354 if ($y === '') { 18355 $y = $this->y; 18356 } 18357 $k = $this->k; 18358 $ox = 0; 18359 $oy = 0; 18360 $ow = $w; 18361 $oh = $h; 18362 $aspect_ratio_align = 'xMidYMid'; 18363 $aspect_ratio_ms = 'meet'; 18364 $regs = array(); 18365 // get original image width and height 18366 preg_match('/<svg([^\>]*)>/si', $svgdata, $regs); 18367 if (isset($regs[1]) AND !empty($regs[1])) { 18368 $tmp = array(); 18369 if (preg_match('/[\s]+x[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) { 18370 $ox = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false); 18371 } 18372 $tmp = array(); 18373 if (preg_match('/[\s]+y[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) { 18374 $oy = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false); 18375 } 18376 $tmp = array(); 18377 if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) { 18378 $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false); 18379 } 18380 $tmp = array(); 18381 if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) { 18382 $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false); 18383 } 18384 $tmp = array(); 18385 $view_box = array(); 18386 if (preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.\-]+)[\s]+([0-9\.\-]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $regs[1], $tmp)) { 18387 if (count($tmp) == 5) { 18388 array_shift($tmp); 18389 foreach ($tmp as $key => $val) { 18390 $view_box[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false); 18391 } 18392 $ox = $view_box[0]; 18393 $oy = $view_box[1]; 18394 } 18395 // get aspect ratio 18396 $tmp = array(); 18397 if (preg_match('/[\s]+preserveAspectRatio[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) { 18398 $aspect_ratio = preg_split("/[\s]+/si", $tmp[1]); 18399 switch (count($aspect_ratio)) { 18400 case 3: { 18401 $aspect_ratio_align = $aspect_ratio[1]; 18402 $aspect_ratio_ms = $aspect_ratio[2]; 18403 break; 18404 } 18405 case 2: { 18406 $aspect_ratio_align = $aspect_ratio[0]; 18407 $aspect_ratio_ms = $aspect_ratio[1]; 18408 break; 18409 } 18410 case 1: { 18411 $aspect_ratio_align = $aspect_ratio[0]; 18412 $aspect_ratio_ms = 'meet'; 18413 break; 18414 } 18415 } 18416 } 18417 } 18418 } 18419 // calculate image width and height on document 18420 if (($w <= 0) AND ($h <= 0)) { 18421 // convert image size to document unit 18422 $w = $ow; 18423 $h = $oh; 18424 } elseif ($w <= 0) { 18425 $w = $h * $ow / $oh; 18426 } elseif ($h <= 0) { 18427 $h = $w * $oh / $ow; 18428 } 18429 // Check whether we need a new page first as this does not fit 18430 $prev_x = $this->x; 18431 if ($this->checkPageBreak($h, $y)) { 18432 $y = $this->y; 18433 if ($this->rtl) { 18434 $x += ($prev_x - $this->x); 18435 } else { 18436 $x += ($this->x - $prev_x); 18437 } 18438 } 18439 // resize image to be contained on a single page 18440 if ($fitonpage) { 18441 $ratio_wh = $w / $h; 18442 if (($y + $h) > $this->PageBreakTrigger) { 18443 $h = $this->PageBreakTrigger - $y; 18444 $w = $h * $ratio_wh; 18445 } 18446 if (($x + $w) > ($this->w - $this->rMargin)) { 18447 $w = $this->w - $this->rMargin - $x; 18448 $h = $w / $ratio_wh; 18449 } 18450 } 18451 // set alignment 18452 $this->img_rb_y = $y + $h; 18453 // set alignment 18454 if ($this->rtl) { 18455 if ($palign == 'L') { 18456 $ximg = $this->lMargin; 18457 } elseif ($palign == 'C') { 18458 $ximg = ($this->w - $w) / 2; 18459 } elseif ($palign == 'R') { 18460 $ximg = $this->w - $this->rMargin - $w; 18461 } else { 18462 $ximg = $this->w - $x - $w; 18463 } 18464 $this->img_rb_x = $ximg; 18465 } else { 18466 if ($palign == 'L') { 18467 $ximg = $this->lMargin; 18468 } elseif ($palign == 'C') { 18469 $ximg = ($this->w - $w) / 2; 18470 } elseif ($palign == 'R') { 18471 $ximg = $this->w - $this->rMargin - $w; 18472 } else { 18473 $ximg = $x; 18474 } 18475 $this->img_rb_x = $ximg + $w; 18476 } 18477 // store current graphic vars 18478 $gvars = $this->getGraphicVars(); 18479 // store SVG position and scale factors 18480 $svgoffset_x = ($ximg - $ox) * $this->k; 18481 $svgoffset_y = -($y - $oy) * $this->k; 18482 if (isset($view_box[2]) AND ($view_box[2] > 0) AND ($view_box[3] > 0)) { 18483 $ow = $view_box[2]; 18484 $oh = $view_box[3]; 18485 } 18486 $svgscale_x = $w / $ow; 18487 $svgscale_y = $h / $oh; 18488 // scaling and alignment 18489 if ($aspect_ratio_align != 'none') { 18490 // store current scaling values 18491 $svgscale_old_x = $svgscale_x; 18492 $svgscale_old_y = $svgscale_y; 18493 // force uniform scaling 18494 if ($aspect_ratio_ms == 'slice') { 18495 // the entire viewport is covered by the viewBox 18496 if ($svgscale_x > $svgscale_y) { 18497 $svgscale_y = $svgscale_x; 18498 } elseif ($svgscale_x < $svgscale_y) { 18499 $svgscale_x = $svgscale_y; 18500 } 18501 } else { // meet 18502 // the entire viewBox is visible within the viewport 18503 if ($svgscale_x < $svgscale_y) { 18504 $svgscale_y = $svgscale_x; 18505 } elseif ($svgscale_x > $svgscale_y) { 18506 $svgscale_x = $svgscale_y; 18507 } 18508 } 18509 // correct X alignment 18510 switch (substr($aspect_ratio_align, 1, 3)) { 18511 case 'Min': { 18512 // do nothing 18513 break; 18514 } 18515 case 'Max': { 18516 $svgoffset_x += (($w * $this->k) - ($ow * $this->k * $svgscale_x)); 18517 break; 18518 } 18519 default: 18520 case 'Mid': { 18521 $svgoffset_x += ((($w * $this->k) - ($ow * $this->k * $svgscale_x)) / 2); 18522 break; 18523 } 18524 } 18525 // correct Y alignment 18526 switch (substr($aspect_ratio_align, 5)) { 18527 case 'Min': { 18528 // do nothing 18529 break; 18530 } 18531 case 'Max': { 18532 $svgoffset_y -= (($h * $this->k) - ($oh * $this->k * $svgscale_y)); 18533 break; 18534 } 18535 default: 18536 case 'Mid': { 18537 $svgoffset_y -= ((($h * $this->k) - ($oh * $this->k * $svgscale_y)) / 2); 18538 break; 18539 } 18540 } 18541 } 18542 // save the current graphic state 18543 $this->_out('q'.$this->epsmarker); 18544 // set initial clipping mask 18545 $this->Rect($x, $y, $w, $h, 'CNZ', array(), array()); 18546 // scale and translate 18547 $e = $ox * $this->k * (1 - $svgscale_x); 18548 $f = ($this->h - $oy) * $this->k * (1 - $svgscale_y); 18549 $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $svgscale_x, 0, 0, $svgscale_y, $e + $svgoffset_x, $f + $svgoffset_y)); 18550 // creates a new XML parser to be used by the other XML functions 18551 $this->parser = xml_parser_create('UTF-8'); 18552 // the following function allows to use parser inside object 18553 xml_set_object($this->parser, $this); 18554 // disable case-folding for this XML parser 18555 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0); 18556 // sets the element handler functions for the XML parser 18557 xml_set_element_handler($this->parser, 'startSVGElementHandler', 'endSVGElementHandler'); 18558 // sets the character data handler function for the XML parser 18559 xml_set_character_data_handler($this->parser, 'segSVGContentHandler'); 18560 // start parsing an XML document 18561 if(!xml_parse($this->parser, $svgdata)) { 18562 $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)); 18563 $this->Error($error_message); 18564 } 18565 // free this XML parser 18566 xml_parser_free($this->parser); 18567 // restore previous graphic state 18568 $this->_out($this->epsmarker.'Q'); 18569 // restore graphic vars 18570 $this->setGraphicVars($gvars); 18571 if (!empty($border)) { 18572 $bx = $x; 18573 $by = $y; 18574 $this->x = $ximg; 18575 if ($this->rtl) { 18576 $this->x += $w; 18577 } 18578 $this->y = $y; 18579 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0); 18580 $this->x = $bx; 18581 $this->y = $by; 18582 } 18583 if ($link) { 18584 $this->Link($ximg, $y, $w, $h, $link, 0); 18585 } 18586 // set pointer to align the successive text/objects 18587 switch($align) { 18588 case 'T':{ 18589 $this->y = $y; 18590 $this->x = $this->img_rb_x; 18591 break; 18592 } 18593 case 'M':{ 18594 $this->y = $y + round($h/2); 18595 $this->x = $this->img_rb_x; 18596 break; 18597 } 18598 case 'B':{ 18599 $this->y = $this->img_rb_y; 18600 $this->x = $this->img_rb_x; 18601 break; 18602 } 18603 case 'N':{ 18604 $this->SetY($this->img_rb_y); 18605 break; 18606 } 18607 default:{ 18608 break; 18609 } 18610 } 18611 $this->endlinex = $this->img_rb_x; 18612 } 18613 18614 /** 18615 * Get the tranformation matrix from SVG transform attribute 18616 * @param string transformation 18617 * @return array of transformations 18618 * @author Nicola Asuni 18619 * @since 5.0.000 (2010-05-02) 18620 * @access protected 18621 */ 18622 protected function getSVGTransformMatrix($attribute) { 18623 // identity matrix 18624 $tm = array(1, 0, 0, 1, 0, 0); 18625 $continue = true; 18626 while ($continue) { 18627 $continue = false; 18628 // matrix 18629 $regs = array(); 18630 if (preg_match('/matrix\(([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)\)/si', $attribute, $regs)) { 18631 $attribute = str_replace($regs[0], '', $attribute); 18632 $continue = true; 18633 $a = $regs[1]; 18634 $b = $regs[2]; 18635 $c = $regs[3]; 18636 $d = $regs[4]; 18637 $e = $regs[5]; 18638 $f = $regs[6]; 18639 $tm = $this->getTransformationMatrixProduct($tm, array($a, $b, $c, $d, $e, $f)); 18640 } 18641 // translate x 18642 $regs = array(); 18643 if (preg_match('/translate\(([a-z0-9\-\.]+)\)/si', $attribute, $regs)) { 18644 $attribute = str_replace($regs[0], 'translate('.$regs[1].',0)', $attribute); 18645 $continue = true; 18646 } 18647 // translate x,y 18648 $regs = array(); 18649 if (preg_match('/translate\(([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)\)/si', $attribute, $regs)) { 18650 $attribute = str_replace($regs[0], '', $attribute); 18651 $continue = true; 18652 $e = $regs[1]; 18653 $f = $regs[2]; 18654 $tm = $this->getTransformationMatrixProduct($tm, array(1, 0, 0, 1, $e, $f)); 18655 } 18656 // scale x 18657 $regs = array(); 18658 if (preg_match('/scale\(([a-z0-9\-\.]+)\)/si', $attribute, $regs)) { 18659 $attribute = str_replace($regs[0], 'scale('.$regs[1].','.$regs[1].')', $attribute); 18660 $continue = true; 18661 } 18662 // scale x,y 18663 $regs = array(); 18664 if (preg_match('/scale\(([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)\)/si', $attribute, $regs)) { 18665 $attribute = str_replace($regs[0], '', $attribute); 18666 $continue = true; 18667 $a = $regs[1]; 18668 if (isset($regs[2]) AND (strlen(trim($regs[2])) > 0)) { 18669 $d = $regs[2]; 18670 } else { 18671 $d = $a; 18672 } 18673 $tm = $this->getTransformationMatrixProduct($tm, array($a, 0, 0, $d, 0, 0)); 18674 } 18675 // rotate ang 18676 $regs = array(); 18677 if (preg_match('/rotate\(([a-z0-9\-\.]+)\)/si', $attribute, $regs)) { 18678 $attribute = str_replace($regs[0], 'rotate('.$regs[1].',0,0)', $attribute); 18679 $continue = true; 18680 } 18681 // rotate ang,x,y 18682 $regs = array(); 18683 if (preg_match('/rotate\(([0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)\)/si', $attribute, $regs)) { 18684 $attribute = str_replace($regs[0], '', $attribute); 18685 $continue = true; 18686 $ang = deg2rad($regs[1]); 18687 $a = cos($ang); 18688 $b = sin($ang); 18689 $c = -$b; 18690 $d = $a; 18691 $x = $regs[2]; 18692 $y = $regs[3]; 18693 $e = ($x * (1 - $a)) - ($y * $c); 18694 $f = ($y * (1 - $d)) - ($x * $b); 18695 $tm = $this->getTransformationMatrixProduct($tm, array($a, $b, $c, $d, $e, $f)); 18696 } 18697 // skewX 18698 $regs = array(); 18699 if (preg_match('/skewX\(([0-9\-\.]+)\)/si', $attribute, $regs)) { 18700 $attribute = str_replace($regs[0], '', $attribute); 18701 $continue = true; 18702 $c = tan(deg2rad($regs[1])); 18703 $tm = $this->getTransformationMatrixProduct($tm, array(1, 0, $c, 1, 0, 0)); 18704 } 18705 // skewY 18706 $regs = array(); 18707 if (preg_match('/skewY\(([0-9\-\.]+)\)/si', $attribute, $regs)) { 18708 $attribute = str_replace($regs[0], '', $attribute); 18709 $continue = true; 18710 $b = tan(deg2rad($regs[1])); 18711 $tm = $this->getTransformationMatrixProduct($tm, array(1, $b, 0, 1, 0, 0)); 18712 } 18713 } 18714 return $tm; 18715 } 18716 18717 /** 18718 * Get the product of two SVG tranformation matrices 18719 * @param array $ta first SVG tranformation matrix 18720 * @param array $tb second SVG tranformation matrix 18721 * @return transformation array 18722 * @author Nicola Asuni 18723 * @since 5.0.000 (2010-05-02) 18724 * @access protected 18725 */ 18726 protected function getTransformationMatrixProduct($ta, $tb) { 18727 $tm = array(); 18728 $tm[0] = ($ta[0] * $tb[0]) + ($ta[2] * $tb[1]); 18729 $tm[1] = ($ta[1] * $tb[0]) + ($ta[3] * $tb[1]); 18730 $tm[2] = ($ta[0] * $tb[2]) + ($ta[2] * $tb[3]); 18731 $tm[3] = ($ta[1] * $tb[2]) + ($ta[3] * $tb[3]); 18732 $tm[4] = ($ta[0] * $tb[4]) + ($ta[2] * $tb[5]) + $ta[4]; 18733 $tm[5] = ($ta[1] * $tb[4]) + ($ta[3] * $tb[5]) + $ta[5]; 18734 return $tm; 18735 } 18736 18737 /** 18738 * Convert SVG transformation matrix to PDF. 18739 * @param array $tm original SVG transformation matrix 18740 * @return array transformation matrix 18741 * @access protected 18742 * @since 5.0.000 (2010-05-02) 18743 */ 18744 protected function convertSVGtMatrix($tm) { 18745 $a = $tm[0]; 18746 $b = -$tm[1]; 18747 $c = -$tm[2]; 18748 $d = $tm[3]; 18749 $e = $this->getHTMLUnitToUnits($tm[4], 1, $this->svgunit, false) * $this->k; 18750 $f = -$this->getHTMLUnitToUnits($tm[5], 1, $this->svgunit, false) * $this->k; 18751 $x = 0; 18752 $y = $this->h * $this->k; 18753 $e = ($x * (1 - $a)) - ($y * $c) + $e; 18754 $f = ($y * (1 - $d)) - ($x * $b) + $f; 18755 return array($a, $b, $c, $d, $e, $f); 18756 } 18757 18758 /** 18759 * Apply SVG graphic transformation matrix. 18760 * @param array $tm original SVG transformation matrix 18761 * @access protected 18762 * @since 5.0.000 (2010-05-02) 18763 */ 18764 protected function SVGTransform($tm) { 18765 $this->Transform($this->convertSVGtMatrix($tm)); 18766 } 18767 18768 /** 18769 * Apply the requested SVG styles (*** TO BE COMPLETED ***) 18770 * @param array $svgstyle array of SVG styles to apply 18771 * @param array $prevsvgstyle array of previous SVG style 18772 * @param int $x X origin of the bounding box 18773 * @param int $y Y origin of the bounding box 18774 * @param int $w width of the bounding box 18775 * @param int $h height of the bounding box 18776 * @param string $clip_function clip function 18777 * @param array $clip_params array of parameters for clipping function 18778 * @return object style 18779 * @author Nicola Asuni 18780 * @since 5.0.000 (2010-05-02) 18781 * @access protected 18782 */ 18783 protected function setSVGStyles($svgstyle, $prevsvgstyle, $x=0, $y=0, $w=1, $h=1, $clip_function='', $clip_params=array()) { 18784 $objstyle = ''; 18785 if(!isset($svgstyle['opacity'])) { 18786 return $objstyle; 18787 } 18788 // clip-path 18789 $regs = array(); 18790 if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['clip-path'], $regs)) { 18791 $clip_path = $this->svgclippaths[$regs[1]]; 18792 foreach ($clip_path as $cp) { 18793 $this->startSVGElementHandler('clip-path', $cp['name'], $cp['attribs']); 18794 } 18795 } 18796 //$this->SetAlpha(1); // reset alpha 18797 // opacity 18798 if ($svgstyle['opacity'] != 1) { 18799 $this->SetAlpha($svgstyle['opacity']); 18800 } 18801 // color 18802 $fill_color = $this->convertHTMLColorToDec($svgstyle['color']); 18803 $this->SetFillColorArray($fill_color); 18804 // text color 18805 $text_color = $this->convertHTMLColorToDec($svgstyle['text-color']); 18806 $this->SetTextColorArray($text_color); 18807 // clip 18808 if (preg_match('/rect\(([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)\)/si', $svgstyle['clip'], $regs)) { 18809 $top = (isset($regs[1])?$this->getHTMLUnitToUnits($regs[1], 0, $this->svgunit, false):0); 18810 $right = (isset($regs[2])?$this->getHTMLUnitToUnits($regs[2], 0, $this->svgunit, false):0); 18811 $bottom = (isset($regs[3])?$this->getHTMLUnitToUnits($regs[3], 0, $this->svgunit, false):0); 18812 $left = (isset($regs[4])?$this->getHTMLUnitToUnits($regs[4], 0, $this->svgunit, false):0); 18813 $cx = $x + $left; 18814 $cy = $y + $top; 18815 $cw = $w - $left - $right; 18816 $ch = $h - $top - $bottom; 18817 if ($svgstyle['clip-rule'] == 'evenodd') { 18818 $clip_rule = 'CNZ'; 18819 } else { 18820 $clip_rule = 'CEO'; 18821 } 18822 $this->Rect($cx, $cy, $cw, $ch, $clip_rule, array(), array()); 18823 } 18824 // fill 18825 $regs = array(); 18826 if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['fill'], $regs)) { 18827 // gradient 18828 $gradient = $this->svggradients[$regs[1]]; 18829 if (isset($gradient['xref'])) { 18830 // reference to another gradient definition 18831 $newgradient = $this->svggradients[$gradient['xref']]; 18832 $newgradient['coords'] = $gradient['coords']; 18833 $newgradient['mode'] = $gradient['mode']; 18834 $newgradient['gradientUnits'] = $gradient['gradientUnits']; 18835 if (isset($gradient['gradientTransform'])) { 18836 $newgradient['gradientTransform'] = $gradient['gradientTransform']; 18837 } 18838 $gradient = $newgradient; 18839 } 18840 //save current Graphic State 18841 $this->_out('q'); 18842 //set clipping area 18843 if (!empty($clip_function) AND method_exists($this, $clip_function)) { 18844 $bbox = call_user_func_array(array($this, $clip_function), $clip_params); 18845 if (is_array($bbox) AND (count($bbox) == 4)) { 18846 list($x, $y, $w, $h) = $bbox; 18847 } 18848 } 18849 if ($gradient['mode'] == 'measure') { 18850 if (isset($gradient['gradientTransform']) AND !empty($gradient['gradientTransform'])) { 18851 $gtm = $gradient['gradientTransform']; 18852 // apply transformation matrix 18853 $xa = ($gtm[0] * $gradient['coords'][0]) + ($gtm[2] * $gradient['coords'][1]) + $gtm[4]; 18854 $ya = ($gtm[1] * $gradient['coords'][0]) + ($gtm[3] * $gradient['coords'][1]) + $gtm[5]; 18855 $xb = ($gtm[0] * $gradient['coords'][2]) + ($gtm[2] * $gradient['coords'][3]) + $gtm[4]; 18856 $yb = ($gtm[1] * $gradient['coords'][2]) + ($gtm[3] * $gradient['coords'][3]) + $gtm[5]; 18857 if (isset($gradient['coords'][4])) { 18858 $gradient['coords'][4] = sqrt(pow(($gtm[0] * $gradient['coords'][4]), 2) + pow(($gtm[1] * $gradient['coords'][4]), 2)); 18859 } 18860 $gradient['coords'][0] = $xa; 18861 $gradient['coords'][1] = $ya; 18862 $gradient['coords'][2] = $xb; 18863 $gradient['coords'][3] = $yb; 18864 18865 } 18866 // convert SVG coordinates to user units 18867 $gradient['coords'][0] = $this->getHTMLUnitToUnits($gradient['coords'][0], 0, $this->svgunit, false); 18868 $gradient['coords'][1] = $this->getHTMLUnitToUnits($gradient['coords'][1], 0, $this->svgunit, false); 18869 $gradient['coords'][2] = $this->getHTMLUnitToUnits($gradient['coords'][2], 0, $this->svgunit, false); 18870 $gradient['coords'][3] = $this->getHTMLUnitToUnits($gradient['coords'][3], 0, $this->svgunit, false); 18871 if (isset($gradient['coords'][4])) { 18872 $gradient['coords'][4] = $this->getHTMLUnitToUnits($gradient['coords'][4], 0, $this->svgunit, false); 18873 } 18874 // shift units 18875 if ($gradient['gradientUnits'] == 'objectBoundingBox') { 18876 // convert to SVG coordinate system 18877 $gradient['coords'][0] += $x; 18878 $gradient['coords'][1] += $y; 18879 $gradient['coords'][2] += $x; 18880 $gradient['coords'][3] += $y; 18881 } 18882 // calculate percentages 18883 $gradient['coords'][0] = ($gradient['coords'][0] - $x) / $w; 18884 $gradient['coords'][1] = ($gradient['coords'][1] - $y) / $h; 18885 $gradient['coords'][2] = ($gradient['coords'][2] - $x) / $w; 18886 $gradient['coords'][3] = ($gradient['coords'][3] - $y) / $h; 18887 if (isset($gradient['coords'][4])) { 18888 $gradient['coords'][4] /= $w; 18889 } 18890 // fix values 18891 foreach($gradient['coords'] as $key => $val) { 18892 if ($val < 0) { 18893 $gradient['coords'][$key] = 0; 18894 } elseif ($val > 1) { 18895 $gradient['coords'][$key] = 1; 18896 } 18897 } 18898 if (($gradient['type'] == 2) AND ($gradient['coords'][0] == $gradient['coords'][2]) AND ($gradient['coords'][1] == $gradient['coords'][3])) { 18899 // single color (no shading) 18900 $gradient['coords'][0] = 1; 18901 $gradient['coords'][1] = 0; 18902 $gradient['coords'][2] = 0.999; 18903 $gradient['coords'][3] = 0; 18904 } 18905 } 18906 // swap Y coordinates 18907 $tmp = $gradient['coords'][1]; 18908 $gradient['coords'][1] = $gradient['coords'][3]; 18909 $gradient['coords'][3] = $tmp; 18910 // set transformation map for gradient 18911 if (($gradient['type'] == 3) AND ($gradient['mode'] == 'measure')) { 18912 // gradient is always circular 18913 $cy = $this->h - $y - ($gradient['coords'][1] * ($w + $h)); 18914 $this->_out(sprintf('%.3F 0 0 %.3F %.3F %.3F cm', $w*$this->k, $w*$this->k, $x*$this->k, $cy*$this->k)); 18915 } else { 18916 $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)); 18917 } 18918 if (count($gradient['stops']) > 1) { 18919 $this->Gradient($gradient['type'], $gradient['coords'], $gradient['stops'], array(), false); 18920 } 18921 } elseif ($svgstyle['fill'] != 'none') { 18922 $fill_color = $this->convertHTMLColorToDec($svgstyle['fill']); 18923 if ($svgstyle['fill-opacity'] != 1) { 18924 $this->SetAlpha($svgstyle['fill-opacity']); 18925 } 18926 $this->SetFillColorArray($fill_color); 18927 if ($svgstyle['fill-rule'] == 'evenodd') { 18928 $objstyle .= 'F*'; 18929 } else { 18930 $objstyle .= 'F'; 18931 } 18932 } 18933 // stroke 18934 if ($svgstyle['stroke'] != 'none') { 18935 $stroke_style = array( 18936 'color' => $this->convertHTMLColorToDec($svgstyle['stroke']), 18937 'width' => $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false), 18938 'cap' => $svgstyle['stroke-linecap'], 18939 'join' => $svgstyle['stroke-linejoin'] 18940 ); 18941 if (isset($svgstyle['stroke-dasharray']) AND !empty($svgstyle['stroke-dasharray']) AND ($svgstyle['stroke-dasharray'] != 'none')) { 18942 $stroke_style['dash'] = $svgstyle['stroke-dasharray']; 18943 } 18944 $this->SetLineStyle($stroke_style); 18945 $objstyle .= 'D'; 18946 } 18947 // font 18948 $regs = array(); 18949 if (!empty($svgstyle['font'])) { 18950 if (preg_match('/font-family[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) { 18951 $font_family = trim($regs[1]); 18952 } else { 18953 $font_family = $svgstyle['font-family']; 18954 } 18955 if (preg_match('/font-size[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) { 18956 $font_size = trim($regs[1]); 18957 } else { 18958 $font_size = $svgstyle['font-size']; 18959 } 18960 if (preg_match('/font-style[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) { 18961 $font_style = trim($regs[1]); 18962 } else { 18963 $font_style = $svgstyle['font-style']; 18964 } 18965 if (preg_match('/font-weight[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) { 18966 $font_weight = trim($regs[1]); 18967 } else { 18968 $font_weight = $svgstyle['font-weight']; 18969 } 18970 } else { 18971 $font_family = $svgstyle['font-family']; 18972 $font_size = $svgstyle['font-size']; 18973 $font_style = $svgstyle['font-style']; 18974 $font_weight = $svgstyle['font-weight']; 18975 } 18976 $font_size = $this->getHTMLUnitToUnits($font_size, $prevsvgstyle['font-size'], $this->svgunit, false) * $this->k; 18977 switch ($font_style) { 18978 case 'italic': { 18979 $font_style = 'I'; 18980 break; 18981 } 18982 case 'oblique': { 18983 $font_style = 'I'; 18984 break; 18985 } 18986 default: 18987 case 'normal': { 18988 $font_style = ''; 18989 break; 18990 } 18991 } 18992 switch ($font_weight) { 18993 case 'bold': 18994 case 'bolder': { 18995 $font_style .= 'B'; 18996 break; 18997 } 18998 } 18999 switch ($svgstyle['text-decoration']) { 19000 case 'underline': { 19001 $font_style .= 'U'; 19002 break; 19003 } 19004 case 'overline': { 19005 $font_style .= 'O'; 19006 break; 19007 } 19008 case 'line-through': { 19009 $font_style .= 'D'; 19010 break; 19011 } 19012 default: 19013 case 'none': { 19014 break; 19015 } 19016 } 19017 $this->SetFont($font_family, $font_style, $font_size); 19018 return $objstyle; 19019 } 19020 19021 /** 19022 * Draws an SVG path 19023 * @param string $d attribute d of the path SVG element 19024 * @param string $style Style of rendering. Possible values are: 19025 * <ul> 19026 * <li>D or empty string: Draw (default).</li> 19027 * <li>F: Fill.</li> 19028 * <li>F*: Fill using the even-odd rule to determine which regions lie inside the clipping path.</li> 19029 * <li>DF or FD: Draw and fill.</li> 19030 * <li>DF* or FD*: Draw and fill using the even-odd rule to determine which regions lie inside the clipping path.</li> 19031 * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li> 19032 * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li> 19033 * </ul> 19034 * @return array of container box measures (x, y, w, h) 19035 * @author Nicola Asuni 19036 * @since 5.0.000 (2010-05-02) 19037 * @access protected 19038 */ 19039 protected function SVGPath($d, $style='') { 19040 // set fill/stroke style 19041 $op = $this->getPathPaintOperator($style, ''); 19042 if (empty($op)) { 19043 return; 19044 } 19045 $paths = array(); 19046 preg_match_all('/([a-zA-Z])[\s]*([^a-zA-Z\"]*)/si', $d, $paths, PREG_SET_ORDER); 19047 $x = 0; 19048 $y = 0; 19049 $x1 = 0; 19050 $y1 = 0; 19051 $x2 = 0; 19052 $y2 = 0; 19053 $xmin = 2147483647; 19054 $xmax = 0; 19055 $ymin = 2147483647; 19056 $ymax = 0; 19057 $relcoord = false; 19058 // draw curve pieces 19059 foreach ($paths as $key => $val) { 19060 // get curve type 19061 $cmd = trim($val[1]); 19062 if (strtolower($cmd) == $cmd) { 19063 // use relative coordinated instead of absolute 19064 $relcoord = true; 19065 $xoffset = $x; 19066 $yoffset = $y; 19067 } else { 19068 $relcoord = false; 19069 $xoffset = 0; 19070 $yoffset = 0; 19071 } 19072 $params = array(); 19073 if (isset($val[2])) { 19074 // get curve parameters 19075 $rawparams = preg_split('/([\,\s]+)/si', trim($val[2])); 19076 $params = array(); 19077 foreach ($rawparams as $ck => $cp) { 19078 $params[$ck] = $this->getHTMLUnitToUnits($cp, 0, $this->svgunit, false); 19079 } 19080 } 19081 switch (strtoupper($cmd)) { 19082 case 'M': { // moveto 19083 foreach ($params as $ck => $cp) { 19084 if (($ck % 2) == 0) { 19085 $x = $cp + $xoffset; 19086 } else { 19087 $y = $cp + $yoffset; 19088 if ($ck == 1) { 19089 $this->_outPoint($x, $y); 19090 } else { 19091 $this->_outLine($x, $y); 19092 } 19093 $xmin = min($xmin, $x); 19094 $ymin = min($ymin, $y); 19095 $xmax = max($xmax, $x); 19096 $ymax = max($ymax, $y); 19097 if ($relcoord) { 19098 $xoffset = $x; 19099 $yoffset = $y; 19100 } 19101 } 19102 } 19103 break; 19104 } 19105 case 'L': { // lineto 19106 foreach ($params as $ck => $cp) { 19107 if (($ck % 2) == 0) { 19108 $x = $cp + $xoffset; 19109 } else { 19110 $y = $cp + $yoffset; 19111 $this->_outLine($x, $y); 19112 $xmin = min($xmin, $x); 19113 $ymin = min($ymin, $y); 19114 $xmax = max($xmax, $x); 19115 $ymax = max($ymax, $y); 19116 if ($relcoord) { 19117 $xoffset = $x; 19118 $yoffset = $y; 19119 } 19120 } 19121 } 19122 break; 19123 } 19124 case 'H': { // horizontal lineto 19125 foreach ($params as $ck => $cp) { 19126 $x = $cp + $xoffset; 19127 $this->_outLine($x, $y); 19128 $xmin = min($xmin, $x); 19129 $xmax = max($xmax, $x); 19130 if ($relcoord) { 19131 $xoffset = $x; 19132 } 19133 } 19134 break; 19135 } 19136 case 'V': { // vertical lineto 19137 foreach ($params as $ck => $cp) { 19138 $y = $cp + $yoffset; 19139 $this->_outLine($x, $y); 19140 $ymin = min($ymin, $y); 19141 $ymax = max($ymax, $y); 19142 if ($relcoord) { 19143 $yoffset = $y; 19144 } 19145 } 19146 break; 19147 } 19148 case 'C': { // curveto 19149 foreach ($params as $ck => $cp) { 19150 $params[$ck] = $cp; 19151 if ((($ck + 1) % 6) == 0) { 19152 $x1 = $params[($ck - 5)] + $xoffset; 19153 $y1 = $params[($ck - 4)] + $yoffset; 19154 $x2 = $params[($ck - 3)] + $xoffset; 19155 $y2 = $params[($ck - 2)] + $yoffset; 19156 $x = $params[($ck - 1)] + $xoffset; 19157 $y = $params[($ck)] + $yoffset; 19158 $this->_outCurve($x1, $y1, $x2, $y2, $x, $y); 19159 $xmin = min($xmin, $x, $x1, $x2); 19160 $ymin = min($ymin, $y, $y1, $y2); 19161 $xmax = max($xmax, $x, $x1, $x2); 19162 $ymax = max($ymax, $y, $y1, $y2); 19163 if ($relcoord) { 19164 $xoffset = $x; 19165 $yoffset = $y; 19166 } 19167 } 19168 } 19169 break; 19170 } 19171 case 'S': { // shorthand/smooth curveto 19172 foreach ($params as $ck => $cp) { 19173 $params[$ck] = $cp; 19174 if ((($ck + 1) % 4) == 0) { 19175 if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'C') OR (strtoupper($paths[($key - 1)][1]) == 'S'))) { 19176 $x1 = (2 * $x) - $x2; 19177 $y1 = (2 * $y) - $y2; 19178 } else { 19179 $x1 = $x; 19180 $y1 = $y; 19181 } 19182 $x2 = $params[($ck - 3)] + $xoffset; 19183 $y2 = $params[($ck - 2)] + $yoffset; 19184 $x = $params[($ck - 1)] + $xoffset; 19185 $y = $params[($ck)] + $yoffset; 19186 $this->_outCurve($x1, $y1, $x2, $y2, $x, $y); 19187 $xmin = min($xmin, $x, $x1, $x2); 19188 $ymin = min($ymin, $y, $y1, $y2); 19189 $xmax = max($xmax, $x, $x1, $x2); 19190 $ymax = max($ymax, $y, $y1, $y2); 19191 if ($relcoord) { 19192 $xoffset = $x; 19193 $yoffset = $y; 19194 } 19195 } 19196 } 19197 break; 19198 } 19199 case 'Q': { // quadratic Bézier curveto 19200 foreach ($params as $ck => $cp) { 19201 $params[$ck] = $cp; 19202 if ((($ck + 1) % 4) == 0) { 19203 // convert quadratic points to cubic points 19204 $x1 = $params[($ck - 3)] + $xoffset; 19205 $y1 = $params[($ck - 2)] + $yoffset; 19206 $xa = ($x + (2 * $x1)) / 3; 19207 $ya = ($y + (2 * $y1)) / 3; 19208 $x = $params[($ck - 1)] + $xoffset; 19209 $y = $params[($ck)] + $yoffset; 19210 $xb = ($x + (2 * $x1)) / 3; 19211 $yb = ($y + (2 * $y1)) / 3; 19212 $this->_outCurve($xa, $ya, $xb, $yb, $x, $y); 19213 $xmin = min($xmin, $x, $xa, $xb); 19214 $ymin = min($ymin, $y, $ya, $yb); 19215 $xmax = max($xmax, $x, $xa, $xb); 19216 $ymax = max($ymax, $y, $ya, $yb); 19217 if ($relcoord) { 19218 $xoffset = $x; 19219 $yoffset = $y; 19220 } 19221 } 19222 } 19223 break; 19224 } 19225 case 'T': { // shorthand/smooth quadratic Bézier curveto 19226 foreach ($params as $ck => $cp) { 19227 $params[$ck] = $cp; 19228 if (($ck % 2) != 0) { 19229 if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'Q') OR (strtoupper($paths[($key - 1)][1]) == 'T'))) { 19230 $x1 = (2 * $x) - $x1; 19231 $y1 = (2 * $y) - $y1; 19232 } else { 19233 $x1 = $x; 19234 $y1 = $y; 19235 } 19236 // convert quadratic points to cubic points 19237 $xa = ($x + (2 * $x1)) / 3; 19238 $ya = ($y + (2 * $y1)) / 3; 19239 $x = $params[($ck - 1)] + $xoffset; 19240 $y = $params[($ck)] + $yoffset; 19241 $xb = ($x + (2 * $x1)) / 3; 19242 $yb = ($y + (2 * $y1)) / 3; 19243 $this->_outCurve($xa, $ya, $xb, $yb, $x, $y); 19244 $xmin = min($xmin, $x, $x1, $x2); 19245 $ymin = min($ymin, $y, $y1, $y2); 19246 $xmax = max($xmax, $x, $x1, $x2); 19247 $ymax = max($ymax, $y, $y1, $y2); 19248 if ($relcoord) { 19249 $xoffset = $x; 19250 $yoffset = $y; 19251 } 19252 } 19253 } 19254 break; 19255 } 19256 case 'A': { // elliptical arc 19257 foreach ($params as $ck => $cp) { 19258 $params[$ck] = $cp; 19259 if ((($ck + 1) % 7) == 0) { 19260 $x0 = $x; 19261 $y0 = $y; 19262 $rx = abs($params[($ck - 6)]); 19263 $ry = abs($params[($ck - 5)]); 19264 $ang = -$rawparams[($ck - 4)]; 19265 $angle = deg2rad($ang); 19266 $fa = $rawparams[($ck - 3)]; // large-arc-flag 19267 $fs = $rawparams[($ck - 2)]; // sweep-flag 19268 $x = $params[($ck - 1)] + $xoffset; 19269 $y = $params[$ck] + $yoffset; 19270 $cos_ang = cos($angle); 19271 $sin_ang = sin($angle); 19272 $a = ($x0 - $x) / 2; 19273 $b = ($y0 - $y) / 2; 19274 $xa = ($a * $cos_ang) - ($b * $sin_ang); 19275 $ya = ($a * $sin_ang) + ($b * $cos_ang); 19276 $rx2 = $rx * $rx; 19277 $ry2 = $ry * $ry; 19278 $xa2 = $xa * $xa; 19279 $ya2 = $ya * $ya; 19280 $delta = ($xa2 / $rx2) + ($ya2 / $ry2); 19281 if ($delta > 1) { 19282 $rx *= sqrt($delta); 19283 $ry *= sqrt($delta); 19284 $rx2 = $rx * $rx; 19285 $ry2 = $ry * $ry; 19286 } 19287 $numerator = (($rx2 * $ry2) - ($rx2 * $ya2) - ($ry2 * $xa2)); 19288 if ($numerator < 0) { 19289 $root = 0; 19290 } else { 19291 $root = sqrt($numerator / (($rx2 * $ya2) + ($ry2 * $xa2))); 19292 } 19293 if ($fa == $fs) { 19294 $root *= -1; 19295 } 19296 $cax = $root * (($rx * $ya) / $ry); 19297 $cay = -$root * (($ry * $xa) / $rx); 19298 // coordinates of ellipse center 19299 $cx = ($cax * $cos_ang) - ($cay * $sin_ang) + (($x0 + $x) / 2); 19300 $cy = ($cax * $sin_ang) + ($cay * $cos_ang) + (($y0 + $y) / 2); 19301 // get angles 19302 $angs = $this->getVectorsAngle(1, 0, (($xa - $cax) / $rx), (($cay - $ya) / $ry)); 19303 $dang = $this->getVectorsAngle((($xa - $cax) / $rx), (($ya - $cay) / $ry), ((-$xa - $cax) / $rx), ((-$ya - $cay) / $ry)); 19304 if (($fs == 0) AND ($dang > 0)) { 19305 $dang -= (2 * M_PI); 19306 } elseif (($fs == 1) AND ($dang < 0)) { 19307 $dang += (2 * M_PI); 19308 } 19309 $angf = $angs - $dang; 19310 if (($fs == 1) AND ($angs > $angf)) { 19311 $tmp = $angs; 19312 $angs = $angf; 19313 $angf = $tmp; 19314 } 19315 $angs = rad2deg($angs); 19316 $angf = rad2deg($angf); 19317 $pie = false; 19318 if ((isset($paths[($key + 1)][1])) AND (trim($paths[($key + 1)][1]) == 'z')) { 19319 $pie = true; 19320 } 19321 $this->_outellipticalarc($cx, $cy, $rx, $ry, $ang, $angs, $angf, $pie, 2); 19322 $this->_outPoint($x, $y); 19323 $xmin = min($xmin, $x); 19324 $ymin = min($ymin, $y); 19325 $xmax = max($xmax, $x); 19326 $ymax = max($ymax, $y); 19327 if ($relcoord) { 19328 $xoffset = $x; 19329 $yoffset = $y; 19330 } 19331 } 19332 } 19333 break; 19334 } 19335 case 'Z': { 19336 $this->_out('h'); 19337 break; 19338 } 19339 } 19340 } // end foreach 19341 if (!empty($op)) { 19342 $this->_out($op); 19343 } 19344 return array($xmin, $ymin, ($xmax - $xmin), ($ymax - $ymin)); 19345 } 19346 19347 /** 19348 * Returns the angle in radiants between two vectors 19349 * @param int $x1 X coordiante of first vector point 19350 * @param int $y1 Y coordiante of first vector point 19351 * @param int $x2 X coordiante of second vector point 19352 * @param int $y2 Y coordiante of second vector point 19353 * @author Nicola Asuni 19354 * @since 5.0.000 (2010-05-04) 19355 * @access protected 19356 */ 19357 protected function getVectorsAngle($x1, $y1, $x2, $y2) { 19358 $dprod = ($x1 * $x2) + ($y1 * $y2); 19359 $dist1 = sqrt(($x1 * $x1) + ($y1 * $y1)); 19360 $dist2 = sqrt(($x2 * $x2) + ($y2 * $y2)); 19361 $angle = acos($dprod / ($dist1 * $dist2)); 19362 if (is_nan($angle)) { 19363 $angle = M_PI; 19364 } 19365 if ((($x1 * $y2) - ($x2 * $y1)) < 0) { 19366 $angle *= -1; 19367 } 19368 return $angle; 19369 } 19370 19371 /** 19372 * Sets the opening SVG element handler function for the XML parser. (*** TO BE COMPLETED ***) 19373 * @param resource $parser The first parameter, parser, is a reference to the XML parser calling the handler. 19374 * @param string $name The second parameter, name, contains the name of the element for which this handler is called. If case-folding is in effect for this parser, the element name will be in uppercase letters. 19375 * @param array $attribs The third parameter, attribs, contains an associative array with the element's attributes (if any). The keys of this array are the attribute names, the values are the attribute values. Attribute names are case-folded on the same criteria as element names. Attribute values are not case-folded. The original order of the attributes can be retrieved by walking through attribs the normal way, using each(). The first key in the array was the first attribute, and so on. 19376 * @author Nicola Asuni 19377 * @since 5.0.000 (2010-05-02) 19378 * @access protected 19379 */ 19380 protected function startSVGElementHandler($parser, $name, $attribs) { 19381 // check if we are in clip mode 19382 if ($this->svgclipmode) { 19383 $this->svgclippaths[$this->svgclipid][] = array('name' => $name, 'attribs' => $attribs); 19384 return; 19385 } 19386 if ($this->svgdefsmode AND !in_array($name, array('clipPath', 'linearGradient', 'radialGradient', 'stop'))) { 19387 $this->svgdefs[$attribs['id']] = array('name' => $name, 'attribs' => $attribs); 19388 return; 19389 } 19390 $clipping = false; 19391 if ($parser == 'clip-path') { 19392 // set clipping mode 19393 $clipping = true; 19394 } 19395 // get styling properties 19396 $prev_svgstyle = $this->svgstyles[(count($this->svgstyles) - 1)]; // previous style 19397 $svgstyle = array(); // current style 19398 if (isset($attribs['style'])) { 19399 $attribs['style'] = ';'.$attribs['style']; 19400 } 19401 foreach ($prev_svgstyle as $key => $val) { 19402 if (isset($attribs[$key])) { 19403 if ($attribs[$key] == 'inherit') { 19404 $svgstyle[$key] = $val; 19405 } else { 19406 $svgstyle[$key] = $attribs[$key]; 19407 } 19408 } elseif (isset($attribs['style'])) { 19409 // CSS style syntax 19410 $attrval = array(); 19411 if (preg_match('/[;\"\s]{1}'.$key.'[\s]*:[\s]*([^;\"\s]*)/si', $attribs['style'], $attrval) AND isset($attrval[1])) { 19412 if ($attrval[1] == 'inherit') { 19413 $svgstyle[$key] = $val; 19414 } else { 19415 $svgstyle[$key] = $attrval[1]; 19416 } 19417 } else { 19418 // default value 19419 $svgstyle[$key] = $this->svgstyles[0][$key]; 19420 } 19421 } else { 19422 if (in_array($key, $this->svginheritprop)) { 19423 // inherit previous value 19424 $svgstyle[$key] = $val; 19425 } else { 19426 // default value 19427 $svgstyle[$key] = $this->svgstyles[0][$key]; 19428 } 19429 } 19430 } 19431 // transformation matrix 19432 $tm = $this->svgstyles[(count($this->svgstyles) - 1)]['transfmatrix']; 19433 if (isset($attribs['transform']) AND !empty($attribs['transform'])) { 19434 $tm = $this->getTransformationMatrixProduct($tm, $this->getSVGTransformMatrix($attribs['transform'])); 19435 } 19436 $svgstyle['transfmatrix'] = $tm; 19437 // process tag 19438 switch($name) { 19439 case 'defs': { 19440 $this->svgdefsmode = true; 19441 break; 19442 } 19443 // clipPath 19444 case 'clipPath': { 19445 $this->svgclipmode = true; 19446 $this->svgclipid = $attribs['id']; 19447 $this->svgclippaths[$this->svgclipid] = array(); 19448 break; 19449 } 19450 case 'svg': { 19451 // start of SVG object 19452 break; 19453 } 19454 case 'g': { 19455 // group together related graphics elements 19456 array_push($this->svgstyles, $svgstyle); 19457 $this->StartTransform(); 19458 $this->setSVGStyles($svgstyle, $prev_svgstyle); 19459 break; 19460 } 19461 case 'linearGradient': { 19462 $this->svggradientid = $attribs['id']; 19463 $this->svggradients[$this->svggradientid] = array(); 19464 $this->svggradients[$this->svggradientid]['type'] = 2; 19465 $this->svggradients[$this->svggradientid]['stops'] = array(); 19466 if (isset($attribs['gradientUnits'])) { 19467 $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits']; 19468 } else { 19469 $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox'; 19470 } 19471 //$attribs['spreadMethod'] 19472 $x1 = (isset($attribs['x1'])?$attribs['x1']:0); 19473 $y1 = (isset($attribs['y1'])?$attribs['y1']:0); 19474 $x2 = (isset($attribs['x2'])?$attribs['x2']:1); 19475 $y2 = (isset($attribs['y2'])?$attribs['y2']:0); 19476 if (isset($attribs['x1']) AND (substr($attribs['x1'], -1) != '%')) { 19477 $this->svggradients[$this->svggradientid]['mode'] = 'measure'; 19478 } else { 19479 $this->svggradients[$this->svggradientid]['mode'] = 'percentage'; 19480 } 19481 if (isset($attribs['gradientTransform'])) { 19482 $this->svggradients[$this->svggradientid]['gradientTransform'] = $this->getSVGTransformMatrix($attribs['gradientTransform']); 19483 } 19484 $this->svggradients[$this->svggradientid]['coords'] = array($x1, $y1, $x2, $y2); 19485 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) { 19486 // gradient is defined on another place 19487 $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1); 19488 } 19489 break; 19490 } 19491 case 'radialGradient': { 19492 $this->svggradientid = $attribs['id']; 19493 $this->svggradients[$this->svggradientid] = array(); 19494 $this->svggradients[$this->svggradientid]['type'] = 3; 19495 $this->svggradients[$this->svggradientid]['stops'] = array(); 19496 if (isset($attribs['gradientUnits'])) { 19497 $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits']; 19498 } else { 19499 $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox'; 19500 } 19501 //$attribs['spreadMethod'] 19502 $cx = (isset($attribs['cx'])?$attribs['cx']:0.5); 19503 $cy = (isset($attribs['cy'])?$attribs['cy']:0.5); 19504 $fx = (isset($attribs['fx'])?$attribs['fx']:$cx); 19505 $fy = (isset($attribs['fy'])?$attribs['fy']:$cy); 19506 $r = (isset($attribs['r'])?$attribs['r']:0.5); 19507 if (isset($attribs['cx']) AND (substr($attribs['cx'], -1) != '%')) { 19508 $this->svggradients[$this->svggradientid]['mode'] = 'measure'; 19509 } else { 19510 $this->svggradients[$this->svggradientid]['mode'] = 'percentage'; 19511 } 19512 if (isset($attribs['gradientTransform'])) { 19513 $this->svggradients[$this->svggradientid]['gradientTransform'] = $this->getSVGTransformMatrix($attribs['gradientTransform']); 19514 } 19515 $this->svggradients[$this->svggradientid]['coords'] = array($cx, $cy, $fx, $fy, $r); 19516 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) { 19517 // gradient is defined on another place 19518 $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1); 19519 } 19520 break; 19521 } 19522 case 'stop': { 19523 // gradient stops 19524 if (substr($attribs['offset'], -1) == '%') { 19525 $offset = floatval(substr($attribs['offset'], -1)) / 100; 19526 } else { 19527 $offset = floatval($attribs['offset']); 19528 if ($offset > 1) { 19529 $offset /= 100; 19530 } 19531 } 19532 $stop_color = isset($svgstyle['stop-color'])?$this->convertHTMLColorToDec($svgstyle['stop-color']):'black'; 19533 $opacity = isset($svgstyle['stop-opacity'])?$svgstyle['stop-opacity']:1; 19534 $this->svggradients[$this->svggradientid]['stops'][] = array('offset' => $offset, 'color' => $stop_color, 'opacity' => $opacity); 19535 break; 19536 } 19537 // paths 19538 case 'path': { 19539 $d = trim($attribs['d']); 19540 if ($clipping) { 19541 $this->SVGTransform($tm); 19542 $this->SVGPath($d, 'CNZ'); 19543 } else { 19544 $this->StartTransform(); 19545 $this->SVGTransform($tm); 19546 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, 0, 0, 1, 1, 'SVGPath', array($d, 'CNZ')); 19547 if (!empty($obstyle)) { 19548 $this->SVGPath($d, $obstyle); 19549 } 19550 $this->StopTransform(); 19551 } 19552 break; 19553 } 19554 // shapes 19555 case 'rect': { 19556 $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0); 19557 $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0); 19558 $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0); 19559 $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0); 19560 $rx = (isset($attribs['rx'])?$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false):0); 19561 $ry = (isset($attribs['ry'])?$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false):$rx); 19562 if ($clipping) { 19563 $this->SVGTransform($tm); 19564 $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ', array(), array()); 19565 } else { 19566 $this->StartTransform(); 19567 $this->SVGTransform($tm); 19568 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'RoundedRectXY', array($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ')); 19569 if (!empty($obstyle)) { 19570 $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', $obstyle, array(), array()); 19571 } 19572 $this->StopTransform(); 19573 } 19574 break; 19575 } 19576 case 'circle': { 19577 $cx = (isset($attribs['cx'])?$this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false):0); 19578 $cy = (isset($attribs['cy'])?$this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false):0); 19579 $r = (isset($attribs['r'])?$this->getHTMLUnitToUnits($attribs['r'], 0, $this->svgunit, false):0); 19580 $x = $cx - $r; 19581 $y = $cy - $r; 19582 $w = 2 * $r; 19583 $h = $w; 19584 if ($clipping) { 19585 $this->SVGTransform($tm); 19586 $this->Circle($cx, $cy, $r, 0, 360, 'CNZ', array(), array(), 8); 19587 } else { 19588 $this->StartTransform(); 19589 $this->SVGTransform($tm); 19590 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Circle', array($cx, $cy, $r, 0, 360, 'CNZ')); 19591 if (!empty($obstyle)) { 19592 $this->Circle($cx, $cy, $r, 0, 360, $obstyle, array(), array(), 8); 19593 } 19594 $this->StopTransform(); 19595 } 19596 break; 19597 } 19598 case 'ellipse': { 19599 $cx = (isset($attribs['cx'])?$this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false):0); 19600 $cy = (isset($attribs['cy'])?$this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false):0); 19601 $rx = (isset($attribs['rx'])?$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false):0); 19602 $ry = (isset($attribs['ry'])?$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false):0); 19603 $x = $cx - $rx; 19604 $y = $cy - $ry; 19605 $w = 2 * $rx; 19606 $h = 2 * $ry; 19607 if ($clipping) { 19608 $this->SVGTransform($tm); 19609 $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ', array(), array(), 8); 19610 } else { 19611 $this->StartTransform(); 19612 $this->SVGTransform($tm); 19613 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Ellipse', array($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ')); 19614 if (!empty($obstyle)) { 19615 $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, $obstyle, array(), array(), 8); 19616 } 19617 $this->StopTransform(); 19618 } 19619 break; 19620 } 19621 case 'line': { 19622 $x1 = (isset($attribs['x1'])?$this->getHTMLUnitToUnits($attribs['x1'], 0, $this->svgunit, false):0); 19623 $y1 = (isset($attribs['y1'])?$this->getHTMLUnitToUnits($attribs['y1'], 0, $this->svgunit, false):0); 19624 $x2 = (isset($attribs['x2'])?$this->getHTMLUnitToUnits($attribs['x2'], 0, $this->svgunit, false):0); 19625 $y2 = (isset($attribs['y2'])?$this->getHTMLUnitToUnits($attribs['y2'], 0, $this->svgunit, false):0); 19626 $x = $x1; 19627 $y = $y1; 19628 $w = abs($x2 - $x1); 19629 $h = abs($y2 - $y1); 19630 if (!$clipping) { 19631 $this->StartTransform(); 19632 $this->SVGTransform($tm); 19633 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Line', array($x1, $y1, $x2, $y2)); 19634 $this->Line($x1, $y1, $x2, $y2); 19635 $this->StopTransform(); 19636 } 19637 break; 19638 } 19639 case 'polyline': 19640 case 'polygon': { 19641 $points = (isset($attribs['points'])?$attribs['points']:'0 0'); 19642 $points = trim($points); 19643 // note that point may use a complex syntax not covered here 19644 $points = preg_split('/[\,\s]+/si', $points); 19645 if (count($points) < 4) { 19646 break; 19647 } 19648 $p = array(); 19649 $xmin = 2147483647; 19650 $xmax = 0; 19651 $ymin = 2147483647; 19652 $ymax = 0; 19653 foreach ($points as $key => $val) { 19654 $p[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false); 19655 if (($key % 2) == 0) { 19656 // X coordinate 19657 $xmin = min($xmin, $p[$key]); 19658 $xmax = max($xmax, $p[$key]); 19659 } else { 19660 // Y coordinate 19661 $ymin = min($ymin, $p[$key]); 19662 $ymax = max($ymax, $p[$key]); 19663 } 19664 } 19665 $x = $xmin; 19666 $y = $ymin; 19667 $w = ($xmax - $xmin); 19668 $h = ($ymax - $ymin); 19669 if ($name == 'polyline') { 19670 $this->StartTransform(); 19671 $this->SVGTransform($tm); 19672 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'PolyLine', array($p, 'CNZ')); 19673 $this->PolyLine($p, 'D', array(), array()); 19674 $this->StopTransform(); 19675 } else { // polygon 19676 if ($clipping) { 19677 $this->SVGTransform($tm); 19678 $this->Polygon($p, 'CNZ', array(), array(), true); 19679 } else { 19680 $this->StartTransform(); 19681 $this->SVGTransform($tm); 19682 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Polygon', array($p, 'CNZ')); 19683 if (!empty($obstyle)) { 19684 $this->Polygon($p, $obstyle, array(), array(), true); 19685 } 19686 $this->StopTransform(); 19687 } 19688 } 19689 break; 19690 } 19691 // image 19692 case 'image': { 19693 if (!isset($attribs['xlink:href']) OR empty($attribs['xlink:href'])) { 19694 break; 19695 } 19696 $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0); 19697 $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0); 19698 $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0); 19699 $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0); 19700 $img = $attribs['xlink:href']; 19701 if (!$clipping) { 19702 $this->StartTransform(); 19703 $this->SVGTransform($tm); 19704 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h); 19705 // fix image path 19706 if (!$this->empty_string($this->svgdir) AND (($img{0} == '.') OR (basename($img) == $img))) { 19707 // replace relative path with full server path 19708 $img = $this->svgdir.'/'.$img; 19709 } 19710 if (($img{0} == '/') AND ($_SERVER['DOCUMENT_ROOT'] != '/')) { 19711 $findroot = strpos($img, $_SERVER['DOCUMENT_ROOT']); 19712 if (($findroot === false) OR ($findroot > 1)) { 19713 // replace relative path with full server path 19714 $img = $_SERVER['DOCUMENT_ROOT'].$img; 19715 } 19716 } 19717 $img = urldecode($img); 19718 $testscrtype = @parse_url($img); 19719 if (!isset($testscrtype['query']) OR empty($testscrtype['query'])) { 19720 // convert URL to server path 19721 $img = str_replace(K_PATH_URL, K_PATH_MAIN, $img); 19722 } 19723 $this->Image($img, $x, $y, $w, $h); 19724 $this->StopTransform(); 19725 } 19726 break; 19727 } 19728 // text 19729 case 'text': 19730 case 'tspan': { 19731 // only basic support - advanced features must be implemented 19732 $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0); 19733 $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0); 19734 $svgstyle['text-color'] = $svgstyle['fill']; 19735 $this->svgtext = ''; 19736 $this->StartTransform(); 19737 $this->SVGTransform($tm); 19738 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, 1, 1); 19739 $this->SetXY($x, $y, true); 19740 break; 19741 } 19742 // use 19743 case 'use': { 19744 if (isset($attribs['xlink:href'])) { 19745 $use = $this->svgdefs[substr($attribs['xlink:href'], 1)]; 19746 if (isset($attribs['xlink:href'])) { 19747 unset($attribs['xlink:href']); 19748 } 19749 if (isset($attribs['id'])) { 19750 unset($attribs['id']); 19751 } 19752 $attribs = array_merge($use['attribs'], $attribs); 19753 $this->startSVGElementHandler($parser, $use['name'], $use['attribs']); 19754 } 19755 break; 19756 } 19757 default: { 19758 break; 19759 } 19760 } 19761 } 19762 19763 /** 19764 * Sets the closing SVG element handler function for the XML parser. 19765 * @param resource $parser The first parameter, parser, is a reference to the XML parser calling the handler. 19766 * @param string $name The second parameter, name, contains the name of the element for which this handler is called. If case-folding is in effect for this parser, the element name will be in uppercase letters. 19767 * @author Nicola Asuni 19768 * @since 5.0.000 (2010-05-02) 19769 * @access protected 19770 */ 19771 protected function endSVGElementHandler($parser, $name) { 19772 switch($name) { 19773 case 'defs': { 19774 $this->svgdefsmode = false; 19775 break; 19776 } 19777 // clipPath 19778 case 'clipPath': { 19779 $this->svgclipmode = false; 19780 break; 19781 } 19782 case 'g': { 19783 // ungroup: remove last style from array 19784 array_pop($this->svgstyles); 19785 $this->StopTransform(); 19786 break; 19787 } 19788 case 'text': 19789 case 'tspan': { 19790 // print text 19791 $this->Cell(0, 0, trim($this->svgtext), 0, 0, '', 0, '', 0, false, 'L', 'T'); 19792 $this->StopTransform(); 19793 break; 19794 } 19795 default: { 19796 break; 19797 } 19798 } 19799 } 19800 19801 /** 19802 * Sets the character data handler function for the XML parser. 19803 * @param resource $parser The first parameter, parser, is a reference to the XML parser calling the handler. 19804 * @param string $data The second parameter, data, contains the character data as a string. 19805 * @author Nicola Asuni 19806 * @since 5.0.000 (2010-05-02) 19807 * @access protected 19808 */ 19809 protected function segSVGContentHandler($parser, $data) { 19810 $this->svgtext .= $data; 19811 } 19812 19813 } // END OF TCPDF CLASS 19814 } 19815 //============================================================+ 19816 // END OF FILE 19817 //============================================================+ 19818 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Tue Mar 17 22:47:18 2015 | Cross-referenced by PHPXref 0.7.1 |