[ Index ] |
PHP Cross Reference of vtigercrm-6.1.0 |
[Summary view] [Print] [Text view]
1 <?php 2 //============================================================+ 3 // File name : tcpdf.php 4 // Begin : 2002-08-03 5 // Last Update : 2009-05-23 6 // Author : Nicola Asuni - [email protected] - http://www.tcpdf.org 7 // Version : 4.6.012 8 // License : GNU LGPL (http://www.gnu.org/copyleft/lesser.html) 9 // ---------------------------------------------------------------------------- 10 // Copyright (C) 2002-2009 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 // * supports all ISO page formats; 39 // * supports custom page formats, margins and units of measure; 40 // * supports UTF-8 Unicode and Right-To-Left languages; 41 // * supports TrueTypeUnicode, OpenTypeUnicode, TrueType, OpenType, Type1 and CID-0 fonts; 42 // * supports document encryption; 43 // * includes methods to publish some XHTML code; 44 // * includes graphic (geometric) and transformation methods; 45 // * includes Javascript and forms support; 46 // * includes a method to print various barcode formats: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extention, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS; 47 // * includes methods to set Bookmarks and print a Table of Content; 48 // * includes methods to move and delete pages; 49 // * includes methods for automatic page header and footer management; 50 // * supports automatic page break; 51 // * supports automatic page numbering and page groups; 52 // * supports automatic line break and text justification; 53 // * supports JPEG and PNG images natively, all images supported by GD (GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM) and all images supported via ImagMagick (http://www.imagemagick.org/www/formats.html) 54 // * supports stroke and clipping mode for text; 55 // * supports clipping masks; 56 // * supports Grayscale, RGB, CMYK, Spot Colors and Transparencies; 57 // * supports several annotations, including links, text and file attachments; 58 // * supports page compression (requires zlib extension); 59 // * supports text hyphenation. 60 // * supports transactions to UNDO commands. 61 // 62 // ----------------------------------------------------------- 63 // THANKS TO: 64 // 65 // Olivier Plathey (http://www.fpdf.org) for original FPDF. 66 // Efthimios Mavrogeorgiadis ([email protected]) for suggestions on RTL language support. 67 // Klemen Vodopivec (http://www.fpdf.de/downloads/addons/37/) for Encryption algorithm. 68 // Warren Sherliker ([email protected]) for better image handling. 69 // dullus for text Justification. 70 // Bob Vincent ([email protected]) for <li> value attribute. 71 // Patrick Benny for text stretch suggestion on Cell(). 72 // Johannes G�ntert for JavaScript support. 73 // Denis Van Nuffelen for Dynamic Form. 74 // Jacek Czekaj for multibyte justification 75 // Anthony Ferrara for the reintroduction of legacy image methods. 76 // Sourceforge user 1707880 (hucste) for line-trough mode. 77 // Larry Stanbery for page groups. 78 // Martin Hall-May for transparency. 79 // Aaron C. Spike for Polycurve method. 80 // Mohamad Ali Golkar, Saleh AlMatrafe, Charles Abbott for Arabic and Persian support. 81 // Moritz Wagner and Andreas Wurmser for graphic functions. 82 // Andrew Whitehead for core fonts support. 83 // Esteban Jo�l Mar�n for OpenType font conversion. 84 // Teus Hagen for several suggestions and fixes. 85 // Yukihiro Nakadaira for CID-0 CJK fonts fixes. 86 // Kosmas Papachristos for some CSS improvements. 87 // Marcel Partap for some fixes. 88 // Won Kyu Park for several suggestions, fixes and patches. 89 // Anyone that has reported a bug or sent a suggestion. 90 //============================================================+ 91 92 /** 93 * This is a PHP class for generating PDF documents without requiring external extensions.<br> 94 * 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> 95 * <h3>TCPDF main features are:</h3> 96 * <ul> 97 * <li>no external libraries are required for the basic functions;</li> 98 * <li>supports all ISO page formats;</li> 99 * <li>supports custom page formats, margins and units of measure;</li> 100 * <li>supports UTF-8 Unicode and Right-To-Left languages;</li> 101 * <li>supports TrueTypeUnicode, OpenTypeUnicode, TrueType, OpenType, Type1 and CID-0 fonts;</li> 102 * <li>supports document encryption;</li> 103 * <li>includes methods to publish some XHTML code;</li> 104 * <li>includes graphic (geometric) and transformation methods;</li> 105 * <li>includes Javascript and forms support;</li> 106 * <li>includes a method to print various barcode formats: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extention, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS;</li> 107 * <li>includes methods to set Bookmarks and print a Table of Content;</li> 108 * <li>includes methods to move and delete pages;</li> 109 * <li>includes methods for automatic page header and footer management;</li> 110 * <li>supports automatic page break;</li> 111 * <li>supports automatic page numbering and page groups;</li> 112 * <li>supports automatic line break and text justification;</li> 113 * <li>supports JPEG and PNG images natively, all images supported by GD (GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM) and all images supported via ImagMagick (http://www.imagemagick.org/www/formats.html)</li> 114 * <li>supports stroke and clipping mode for text;</li> 115 * <li>supports clipping masks;</li> 116 * <li>supports Grayscale, RGB, CMYK, Spot Colors and Transparencies;</li> 117 * <li>supports several annotations, including links, text and file attachments;</li> 118 * <li>supports page compression (requires zlib extension);</li> 119 * <li>supports text hyphenation.</li> 120 * <li>supports transactions to UNDO commands.</li> 121 * </ul> 122 * Tools to encode your unicode fonts are on fonts/utils directory.</p> 123 * @package com.tecnick.tcpdf 124 * @abstract Class for generating PDF files on-the-fly without requiring external extensions. 125 * @author Nicola Asuni 126 * @copyright 2002-2009 Nicola Asuni - Tecnick.com S.r.l (www.tecnick.com) Via Della Pace, 11 - 09044 - Quartucciu (CA) - ITALY - www.tecnick.com - [email protected] 127 * @link http://www.tcpdf.org 128 * @license http://www.gnu.org/copyleft/lesser.html LGPL 129 * @version 4.6.012 130 */ 131 132 /** 133 * main configuration file 134 */ 135 require_once(dirname(__FILE__).'/config/tcpdf_config.php'); 136 137 // includes some support files 138 139 /** 140 * unicode data 141 */ 142 require_once(dirname(__FILE__).'/unicode_data.php'); 143 144 /** 145 * html colors table 146 */ 147 require_once(dirname(__FILE__).'/htmlcolors.php'); 148 149 if (!class_exists('TCPDF', false)) { 150 /** 151 * define default PDF document producer 152 */ 153 define('PDF_PRODUCER', 'TCPDF 4.6.012 (http://www.tcpdf.org)'); 154 155 /** 156 * This is a PHP class for generating PDF documents without requiring external extensions.<br> 157 * 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> 158 * @name TCPDF 159 * @package com.tecnick.tcpdf 160 * @version 4.6.012 161 * @author Nicola Asuni - [email protected] 162 * @link http://www.tcpdf.org 163 * @license http://www.gnu.org/copyleft/lesser.html LGPL 164 */ 165 class TCPDF { 166 167 // protected or Protected properties 168 169 /** 170 * @var current page number 171 * @access protected 172 */ 173 protected $page; 174 175 /** 176 * @var current object number 177 * @access protected 178 */ 179 protected $n; 180 181 /** 182 * @var array of object offsets 183 * @access protected 184 */ 185 protected $offsets; 186 187 /** 188 * @var buffer holding in-memory PDF 189 * @access protected 190 */ 191 protected $buffer; 192 193 /** 194 * @var array containing pages 195 * @access protected 196 */ 197 protected $pages = array(); 198 199 /** 200 * @var current document state 201 * @access protected 202 */ 203 protected $state; 204 205 /** 206 * @var compression flag 207 * @access protected 208 */ 209 protected $compress; 210 211 /** 212 * @var current page orientation (P = Portrait, L = Landscape) 213 * @access protected 214 */ 215 protected $CurOrientation; 216 217 /** 218 * @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> 219 * @access protected 220 */ 221 protected $pagedim = array(); 222 223 /** 224 * @var scale factor (number of points in user unit) 225 * @access protected 226 */ 227 protected $k; 228 229 /** 230 * @var width of page format in points 231 * @access protected 232 */ 233 protected $fwPt; 234 235 /** 236 * @var height of page format in points 237 * @access protected 238 */ 239 protected $fhPt; 240 241 /** 242 * @var current width of page in points 243 * @access protected 244 */ 245 protected $wPt; 246 247 /** 248 * @var current height of page in points 249 * @access protected 250 */ 251 protected $hPt; 252 253 /** 254 * @var current width of page in user unit 255 * @access protected 256 */ 257 protected $w; 258 259 /** 260 * @var current height of page in user unit 261 * @access protected 262 */ 263 protected $h; 264 265 /** 266 * @var left margin 267 * @access protected 268 */ 269 protected $lMargin; 270 271 /** 272 * @var top margin 273 * @access protected 274 */ 275 protected $tMargin; 276 277 /** 278 * @var right margin 279 * @access protected 280 */ 281 protected $rMargin; 282 283 /** 284 * @var page break margin 285 * @access protected 286 */ 287 protected $bMargin; 288 289 /** 290 * @var cell internal padding 291 * @access protected 292 */ 293 //protected 294 public $cMargin; 295 296 /** 297 * @var cell internal padding (previous value) 298 * @access protected 299 */ 300 protected $oldcMargin; 301 302 /** 303 * @var current horizontal position in user unit for cell positioning 304 * @access protected 305 */ 306 protected $x; 307 308 /** 309 * @var current vertical position in user unit for cell positioning 310 * @access protected 311 */ 312 protected $y; 313 314 /** 315 * @var height of last cell printed 316 * @access protected 317 */ 318 protected $lasth; 319 320 /** 321 * @var line width in user unit 322 * @access protected 323 */ 324 protected $LineWidth; 325 326 /** 327 * @var array of standard font names 328 * @access protected 329 */ 330 protected $CoreFonts; 331 332 /** 333 * @var array of used fonts 334 * @access protected 335 */ 336 protected $fonts = array(); 337 338 /** 339 * @var array of font files 340 * @access protected 341 */ 342 protected $FontFiles = array(); 343 344 /** 345 * @var array of encoding differences 346 * @access protected 347 */ 348 protected $diffs = array(); 349 350 /** 351 * @var array of used images 352 * @access protected 353 */ 354 protected $images = array(); 355 356 /** 357 * @var array of Annotations in pages 358 * @access protected 359 */ 360 protected $PageAnnots = array(); 361 362 /** 363 * @var array of internal links 364 * @access protected 365 */ 366 protected $links = array(); 367 368 /** 369 * @var current font family 370 * @access protected 371 */ 372 protected $FontFamily; 373 374 /** 375 * @var current font style 376 * @access protected 377 */ 378 protected $FontStyle; 379 380 /** 381 * @var current font ascent (distance between font top and baseline) 382 * @access protected 383 * @since 2.8.000 (2007-03-29) 384 */ 385 protected $FontAscent; 386 387 /** 388 * @var current font descent (distance between font bottom and baseline) 389 * @access protected 390 * @since 2.8.000 (2007-03-29) 391 */ 392 protected $FontDescent; 393 394 /** 395 * @var underlining flag 396 * @access protected 397 */ 398 protected $underline; 399 400 /** 401 * @var current font info 402 * @access protected 403 */ 404 protected $CurrentFont; 405 406 /** 407 * @var current font size in points 408 * @access protected 409 */ 410 protected $FontSizePt; 411 412 /** 413 * @var current font size in user unit 414 * @access protected 415 */ 416 protected $FontSize; 417 418 /** 419 * @var commands for drawing color 420 * @access protected 421 */ 422 protected $DrawColor; 423 424 /** 425 * @var commands for filling color 426 * @access protected 427 */ 428 protected $FillColor; 429 430 /** 431 * @var commands for text color 432 * @access protected 433 */ 434 protected $TextColor; 435 436 /** 437 * @var indicates whether fill and text colors are different 438 * @access protected 439 */ 440 protected $ColorFlag; 441 442 /** 443 * @var automatic page breaking 444 * @access protected 445 */ 446 protected $AutoPageBreak; 447 448 /** 449 * @var threshold used to trigger page breaks 450 * @access protected 451 */ 452 protected $PageBreakTrigger; 453 454 /** 455 * @var flag set when processing footer 456 * @access protected 457 */ 458 protected $InFooter = false; 459 460 /** 461 * @var zoom display mode 462 * @access protected 463 */ 464 protected $ZoomMode; 465 466 /** 467 * @var layout display mode 468 * @access protected 469 */ 470 protected $LayoutMode; 471 472 /** 473 * @var title 474 * @access protected 475 */ 476 protected $title = ''; 477 478 /** 479 * @var subject 480 * @access protected 481 */ 482 protected $subject = ''; 483 484 /** 485 * @var author 486 * @access protected 487 */ 488 protected $author = ''; 489 490 /** 491 * @var keywords 492 * @access protected 493 */ 494 protected $keywords = ''; 495 496 /** 497 * @var creator 498 * @access protected 499 */ 500 protected $creator = ''; 501 502 /** 503 * @var alias for total number of pages 504 * @access protected 505 */ 506 protected $AliasNbPages = '{nb}'; 507 508 /** 509 * @var alias for page number 510 * @access protected 511 */ 512 protected $AliasNumPage = '{pnb}'; 513 514 /** 515 * @var right-bottom corner X coordinate of inserted image 516 * @since 2002-07-31 517 * @author Nicola Asuni 518 * @access protected 519 */ 520 protected $img_rb_x; 521 522 /** 523 * @var right-bottom corner Y coordinate of inserted image 524 * @since 2002-07-31 525 * @author Nicola Asuni 526 * @access protected 527 */ 528 protected $img_rb_y; 529 530 /** 531 * @var adjusting factor to convert pixels to user units. 532 * @since 2004-06-14 533 * @author Nicola Asuni 534 * @access protected 535 */ 536 protected $imgscale = 1; 537 538 /** 539 * @var boolean set to true when the input text is unicode (require unicode fonts) 540 * @since 2005-01-02 541 * @author Nicola Asuni 542 * @access protected 543 */ 544 protected $isunicode = false; 545 546 /** 547 * @var PDF version 548 * @since 1.5.3 549 * @access protected 550 */ 551 protected $PDFVersion = '1.7'; 552 553 554 // ---------------------- 555 556 /** 557 * @var Minimum distance between header and top page margin. 558 * @access protected 559 */ 560 protected $header_margin; 561 562 /** 563 * @var Minimum distance between footer and bottom page margin. 564 * @access protected 565 */ 566 protected $footer_margin; 567 568 /** 569 * @var original left margin value 570 * @access protected 571 * @since 1.53.0.TC013 572 */ 573 protected $original_lMargin; 574 575 /** 576 * @var original right margin value 577 * @access protected 578 * @since 1.53.0.TC013 579 */ 580 protected $original_rMargin; 581 582 /** 583 * @var Header font. 584 * @access protected 585 */ 586 protected $header_font; 587 588 /** 589 * @var Footer font. 590 * @access protected 591 */ 592 protected $footer_font; 593 594 /** 595 * @var Language templates. 596 * @access protected 597 */ 598 protected $l; 599 600 /** 601 * @var Barcode to print on page footer (only if set). 602 * @access protected 603 */ 604 protected $barcode = false; 605 606 /** 607 * @var If true prints header 608 * @access protected 609 */ 610 protected $print_header = true; 611 612 /** 613 * @var If true prints footer. 614 * @access protected 615 */ 616 protected $print_footer = true; 617 618 /** 619 * @var Header image logo. 620 * @access protected 621 */ 622 protected $header_logo = ''; 623 624 /** 625 * @var Header image logo width in mm. 626 * @access protected 627 */ 628 protected $header_logo_width = 30; 629 630 /** 631 * @var String to print as title on document header. 632 * @access protected 633 */ 634 protected $header_title = ''; 635 636 /** 637 * @var String to print on document header. 638 * @access protected 639 */ 640 protected $header_string = ''; 641 642 /** 643 * @var Default number of columns for html table. 644 * @access protected 645 */ 646 protected $default_table_columns = 4; 647 648 649 // variables for html parser 650 651 /** 652 * @var HTML PARSER: array to store current link and rendering styles. 653 * @access protected 654 */ 655 protected $HREF = array(); 656 657 /** 658 * @var store a list of available fonts on filesystem. 659 * @access protected 660 */ 661 protected $fontlist = array(); 662 663 /** 664 * @var current foreground color 665 * @access protected 666 */ 667 protected $fgcolor; 668 669 /** 670 * @var HTML PARSER: array of boolean values, true in case of ordered list (OL), false otherwise. 671 * @access protected 672 */ 673 protected $listordered = array(); 674 675 /** 676 * @var HTML PARSER: array count list items on nested lists. 677 * @access protected 678 */ 679 protected $listcount = array(); 680 681 /** 682 * @var HTML PARSER: current list nesting level. 683 * @access protected 684 */ 685 protected $listnum = 0; 686 687 /** 688 * @var HTML PARSER: indent amount for lists. 689 * @access protected 690 */ 691 protected $listindent; 692 693 /** 694 * @var current background color 695 * @access protected 696 */ 697 protected $bgcolor; 698 699 /** 700 * @var Store temporary font size in points. 701 * @access protected 702 */ 703 protected $tempfontsize = 10; 704 705 /** 706 * @var spacer for LI tags. 707 * @access protected 708 */ 709 protected $lispacer = ''; 710 711 /** 712 * @var default encoding 713 * @access protected 714 * @since 1.53.0.TC010 715 */ 716 protected $encoding = 'UTF-8'; 717 718 /** 719 * @var PHP internal encoding 720 * @access protected 721 * @since 1.53.0.TC016 722 */ 723 protected $internal_encoding; 724 725 /** 726 * @var indicates if the document language is Right-To-Left 727 * @access protected 728 * @since 2.0.000 729 */ 730 protected $rtl = false; 731 732 /** 733 * @var used to force RTL or LTR string inversion 734 * @access protected 735 * @since 2.0.000 736 */ 737 protected $tmprtl = false; 738 739 // --- Variables used for document encryption: 740 741 /** 742 * Indicates whether document is protected 743 * @access protected 744 * @since 2.0.000 (2008-01-02) 745 */ 746 protected $encrypted; 747 748 /** 749 * U entry in pdf document 750 * @access protected 751 * @since 2.0.000 (2008-01-02) 752 */ 753 protected $Uvalue; 754 755 /** 756 * O entry in pdf document 757 * @access protected 758 * @since 2.0.000 (2008-01-02) 759 */ 760 protected $Ovalue; 761 762 /** 763 * P entry in pdf document 764 * @access protected 765 * @since 2.0.000 (2008-01-02) 766 */ 767 protected $Pvalue; 768 769 /** 770 * encryption object id 771 * @access protected 772 * @since 2.0.000 (2008-01-02) 773 */ 774 protected $enc_obj_id; 775 776 /** 777 * last RC4 key encrypted (cached for optimisation) 778 * @access protected 779 * @since 2.0.000 (2008-01-02) 780 */ 781 protected $last_rc4_key; 782 783 /** 784 * last RC4 computed key 785 * @access protected 786 * @since 2.0.000 (2008-01-02) 787 */ 788 protected $last_rc4_key_c; 789 790 /** 791 * RC4 padding 792 * @access protected 793 */ 794 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"; 795 796 /** 797 * RC4 encryption key 798 * @access protected 799 */ 800 protected $encryption_key; 801 802 // --- bookmark --- 803 804 /** 805 * Outlines for bookmark 806 * @access protected 807 * @since 2.1.002 (2008-02-12) 808 */ 809 protected $outlines = array(); 810 811 /** 812 * Outline root for bookmark 813 * @access protected 814 * @since 2.1.002 (2008-02-12) 815 */ 816 protected $OutlineRoot; 817 818 819 // --- javascript and form --- 820 821 /** 822 * javascript code 823 * @access protected 824 * @since 2.1.002 (2008-02-12) 825 */ 826 protected $javascript = ''; 827 828 /** 829 * javascript counter 830 * @access protected 831 * @since 2.1.002 (2008-02-12) 832 */ 833 protected $n_js; 834 835 /** 836 * line trough state 837 * @access protected 838 * @since 2.8.000 (2008-03-19) 839 */ 840 protected $linethrough; 841 842 // --- Variables used for User's Rights --- 843 // See PDF reference chapter 8.7 Digital Signatures 844 845 /** 846 * If true enables user's rights on PDF reader 847 * @access protected 848 * @since 2.9.000 (2008-03-26) 849 */ 850 protected $ur; 851 852 /** 853 * Names specifying additional document-wide usage rights for the document. 854 * @access protected 855 * @since 2.9.000 (2008-03-26) 856 */ 857 protected $ur_document; 858 859 /** 860 * Names specifying additional annotation-related usage rights for the document. 861 * @access protected 862 * @since 2.9.000 (2008-03-26) 863 */ 864 protected $ur_annots; 865 866 /** 867 * Names specifying additional form-field-related usage rights for the document. 868 * @access protected 869 * @since 2.9.000 (2008-03-26) 870 */ 871 protected $ur_form; 872 873 /** 874 * Names specifying additional signature-related usage rights for the document. 875 * @access protected 876 * @since 2.9.000 (2008-03-26) 877 */ 878 protected $ur_signature; 879 880 /** 881 * Dot Per Inch Document Resolution (do not change) 882 * @access protected 883 * @since 3.0.000 (2008-03-27) 884 */ 885 protected $dpi = 72; 886 887 /** 888 * Array of page numbers were a new page group was started 889 * @access protected 890 * @since 3.0.000 (2008-03-27) 891 */ 892 protected $newpagegroup = array(); 893 894 /** 895 * Contains the number of pages of the groups 896 * @access protected 897 * @since 3.0.000 (2008-03-27) 898 */ 899 protected $pagegroups; 900 901 /** 902 * Contains the alias of the current page group 903 * @access protected 904 * @since 3.0.000 (2008-03-27) 905 */ 906 protected $currpagegroup; 907 908 /** 909 * Restrict the rendering of some elements to screen or printout. 910 * @access protected 911 * @since 3.0.000 (2008-03-27) 912 */ 913 protected $visibility = 'all'; 914 915 /** 916 * Print visibility. 917 * @access protected 918 * @since 3.0.000 (2008-03-27) 919 */ 920 protected $n_ocg_print; 921 922 /** 923 * View visibility. 924 * @access protected 925 * @since 3.0.000 (2008-03-27) 926 */ 927 protected $n_ocg_view; 928 929 /** 930 * Array of transparency objects and parameters. 931 * @access protected 932 * @since 3.0.000 (2008-03-27) 933 */ 934 protected $extgstates; 935 936 /** 937 * Set the default JPEG compression quality (1-100) 938 * @access protected 939 * @since 3.0.000 (2008-03-27) 940 */ 941 protected $jpeg_quality; 942 943 /** 944 * Default cell height ratio. 945 * @access protected 946 * @since 3.0.014 (2008-05-23) 947 */ 948 protected $cell_height_ratio = K_CELL_HEIGHT_RATIO; 949 950 /** 951 * PDF viewer preferences. 952 * @access protected 953 * @since 3.1.000 (2008-06-09) 954 */ 955 protected $viewer_preferences; 956 957 /** 958 * A name object specifying how the document should be displayed when opened. 959 * @access protected 960 * @since 3.1.000 (2008-06-09) 961 */ 962 protected $PageMode; 963 964 /** 965 * Array for storing gradient information. 966 * @access protected 967 * @since 3.1.000 (2008-06-09) 968 */ 969 protected $gradients = array(); 970 971 /** 972 * Array used to store positions inside the pages buffer. 973 * keys are the page numbers 974 * @access protected 975 * @since 3.2.000 (2008-06-26) 976 */ 977 protected $intmrk = array(); 978 979 /** 980 * Array used to store footer positions of each page. 981 * @access protected 982 * @since 3.2.000 (2008-07-01) 983 */ 984 protected $footerpos = array(); 985 986 987 /** 988 * Array used to store footer lenght of each page. 989 * @access protected 990 * @since 4.0.014 (2008-07-29) 991 */ 992 protected $footerlen = array(); 993 994 /** 995 * True if a newline is created. 996 * @access protected 997 * @since 3.2.000 (2008-07-01) 998 */ 999 protected $newline = true; 1000 1001 /** 1002 * End position of the latest inserted line 1003 * @access protected 1004 * @since 3.2.000 (2008-07-01) 1005 */ 1006 protected $endlinex = 0; 1007 1008 /** 1009 * PDF string for last line width 1010 * @access protected 1011 * @since 4.0.006 (2008-07-16) 1012 */ 1013 protected $linestyleWidth = ''; 1014 1015 /** 1016 * PDF string for last line width 1017 * @access protected 1018 * @since 4.0.006 (2008-07-16) 1019 */ 1020 protected $linestyleCap = '0 J'; 1021 1022 /** 1023 * PDF string for last line width 1024 * @access protected 1025 * @since 4.0.006 (2008-07-16) 1026 */ 1027 protected $linestyleJoin = '0 j'; 1028 1029 /** 1030 * PDF string for last line width 1031 * @access protected 1032 * @since 4.0.006 (2008-07-16) 1033 */ 1034 protected $linestyleDash = '[] 0 d'; 1035 1036 /** 1037 * True if marked-content sequence is open 1038 * @access protected 1039 * @since 4.0.013 (2008-07-28) 1040 */ 1041 protected $openMarkedContent = false; 1042 1043 /** 1044 * Count the latest inserted vertical spaces on HTML 1045 * @access protected 1046 * @since 4.0.021 (2008-08-24) 1047 */ 1048 protected $htmlvspace = 0; 1049 1050 /** 1051 * Array of Spot colors 1052 * @access protected 1053 * @since 4.0.024 (2008-09-12) 1054 */ 1055 protected $spot_colors = array(); 1056 1057 /** 1058 * Symbol used for HTML unordered list items 1059 * @access protected 1060 * @since 4.0.028 (2008-09-26) 1061 */ 1062 protected $lisymbol = ''; 1063 1064 /** 1065 * String used to mark the beginning and end of EPS image blocks 1066 * @access protected 1067 * @since 4.1.000 (2008-10-18) 1068 */ 1069 protected $epsmarker = 'x#!#EPS#!#x'; 1070 1071 /** 1072 * Array of transformation matrix 1073 * @access protected 1074 * @since 4.2.000 (2008-10-29) 1075 */ 1076 protected $transfmatrix = array(); 1077 1078 /** 1079 * Booklet mode for double-sided pages 1080 * @access protected 1081 * @since 4.2.000 (2008-10-29) 1082 */ 1083 protected $booklet = false; 1084 1085 /** 1086 * Epsilon value used for float calculations 1087 * @access protected 1088 * @since 4.2.000 (2008-10-29) 1089 */ 1090 protected $feps = 0.001; 1091 1092 /** 1093 * Array used for custom vertical spaces for HTML tags 1094 * @access protected 1095 * @since 4.2.001 (2008-10-30) 1096 */ 1097 protected $tagvspaces = array(); 1098 1099 /** 1100 * @var HTML PARSER: custom indent amount for lists. 1101 * Negative value means disabled. 1102 * @access protected 1103 * @since 4.2.007 (2008-11-12) 1104 */ 1105 protected $customlistindent = -1; 1106 1107 /** 1108 * @var if true keeps the border open for the cell sides that cross the page. 1109 * @access protected 1110 * @since 4.2.010 (2008-11-14) 1111 */ 1112 protected $opencell = true; 1113 1114 /** 1115 * @var array of files to embedd 1116 * @access protected 1117 * @since 4.4.000 (2008-12-07) 1118 */ 1119 protected $embeddedfiles = array(); 1120 1121 /** 1122 * @var boolean true when inside html pre tag 1123 * @access protected 1124 * @since 4.4.001 (2008-12-08) 1125 */ 1126 protected $premode = false; 1127 1128 /** 1129 * Array used to store positions of graphics transformation blocks inside the page buffer. 1130 * keys are the page numbers 1131 * @access protected 1132 * @since 4.4.002 (2008-12-09) 1133 */ 1134 protected $transfmrk = array(); 1135 1136 /** 1137 * Default color for html links 1138 * @access protected 1139 * @since 4.4.003 (2008-12-09) 1140 */ 1141 protected $htmlLinkColorArray = array(0, 0, 255); 1142 1143 /** 1144 * Default font style to add to html links 1145 * @access protected 1146 * @since 4.4.003 (2008-12-09) 1147 */ 1148 protected $htmlLinkFontStyle = 'U'; 1149 1150 /** 1151 * Counts the number of pages. 1152 * @access protected 1153 * @since 4.5.000 (2008-12-31) 1154 */ 1155 protected $numpages = 0; 1156 1157 /** 1158 * Array containing page lenghts in bytes. 1159 * @access protected 1160 * @since 4.5.000 (2008-12-31) 1161 */ 1162 protected $pagelen = array(); 1163 1164 /** 1165 * Counts the number of pages. 1166 * @access protected 1167 * @since 4.5.000 (2008-12-31) 1168 */ 1169 protected $numimages = 0; 1170 1171 /** 1172 * Store the image keys. 1173 * @access protected 1174 * @since 4.5.000 (2008-12-31) 1175 */ 1176 protected $imagekeys = array(); 1177 1178 /** 1179 * Lenght of the buffer in bytes. 1180 * @access protected 1181 * @since 4.5.000 (2008-12-31) 1182 */ 1183 protected $bufferlen = 0; 1184 1185 /** 1186 * If true enables disk caching. 1187 * @access protected 1188 * @since 4.5.000 (2008-12-31) 1189 */ 1190 protected $diskcache = false; 1191 1192 /** 1193 * Counts the number of fonts. 1194 * @access protected 1195 * @since 4.5.000 (2009-01-02) 1196 */ 1197 protected $numfonts = 0; 1198 1199 /** 1200 * Store the font keys. 1201 * @access protected 1202 * @since 4.5.000 (2009-01-02) 1203 */ 1204 protected $fontkeys = array(); 1205 1206 /** 1207 * Store the fage status (true when opened, false when closed). 1208 * @access protected 1209 * @since 4.5.000 (2009-01-02) 1210 */ 1211 protected $pageopen = array(); 1212 1213 /** 1214 * Default monospaced font 1215 * @access protected 1216 * @since 4.5.025 (2009-03-10) 1217 */ 1218 protected $default_monospaced_font = 'courier'; 1219 1220 /** 1221 * Used to store a cloned copy of the current class object 1222 * @access protected 1223 * @since 4.5.029 (2009-03-19) 1224 */ 1225 protected $objcopy; 1226 1227 /** 1228 * Array used to store the lenghts of cache files 1229 * @access protected 1230 * @since 4.5.029 (2009-03-19) 1231 */ 1232 protected $cache_file_lenght = array(); 1233 1234 /** 1235 * Table header content to be repeated on each new page 1236 * @access protected 1237 * @since 4.5.030 (2009-03-20) 1238 */ 1239 protected $thead = ''; 1240 1241 /** 1242 * Distance between the top of page and end of table headers on a new page. 1243 * @access protected 1244 * @since 4.5.030 (2009-03-20) 1245 */ 1246 protected $theadMargin = ''; 1247 1248 /** 1249 * Cache array for UTF8StringToArray() method. 1250 * @access protected 1251 * @since 4.5.037 (2009-04-07) 1252 */ 1253 protected $cache_UTF8StringToArray = array(); 1254 1255 /** 1256 * Maximum size of cache array used for UTF8StringToArray() method. 1257 * @access protected 1258 * @since 4.5.037 (2009-04-07) 1259 */ 1260 protected $cache_maxsize_UTF8StringToArray = 8; 1261 1262 /** 1263 * Current size of cache array used for UTF8StringToArray() method. 1264 * @access protected 1265 * @since 4.5.037 (2009-04-07) 1266 */ 1267 protected $cache_size_UTF8StringToArray = 0; 1268 1269 /** 1270 * If true enables document signing 1271 * @access protected 1272 * @since 4.6.005 (2009-04-24) 1273 */ 1274 protected $sign = false; 1275 1276 /** 1277 * Signature data 1278 * @access protected 1279 * @since 4.6.005 (2009-04-24) 1280 */ 1281 protected $signature_data = array(); 1282 1283 /** 1284 * Signature max lenght 1285 * @access protected 1286 * @since 4.6.005 (2009-04-24) 1287 */ 1288 protected $signature_max_lenght = 5120; 1289 1290 /** 1291 * Regular expression used to find blank characters used for word-wrapping. 1292 * @access protected 1293 * @since 4.6.006 (2009-04-28) 1294 */ 1295 protected $re_spaces = '/[\s\p{Z}\p{Lo}]/'; 1296 1297 //------------------------------------------------------------ 1298 // METHODS 1299 //------------------------------------------------------------ 1300 1301 /** 1302 * This is the class constructor. 1303 * It allows to set up the page format, the orientation and 1304 * the measure unit used in all the methods (except for the font sizes). 1305 * @since 1.0 1306 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li></ul> 1307 * @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. 1308 * @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> 1309 * @param boolean $unicode TRUE means that the input text is unicode (default = true) 1310 * @param boolean $diskcache if TRUE reduce the RAM memory usage by caching temporary data on filesystem (slower). 1311 * @param String $encoding charset encoding; default is UTF-8 1312 * @access public 1313 */ 1314 public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false) { 1315 /* Set internal character encoding to ASCII */ 1316 if (function_exists('mb_internal_encoding') AND mb_internal_encoding()) { 1317 $this->internal_encoding = mb_internal_encoding(); 1318 mb_internal_encoding('ASCII'); 1319 } 1320 // set disk caching 1321 $this->diskcache = $diskcache ? true : false; 1322 // set language direction 1323 $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false; 1324 $this->tmprtl = false; 1325 //Some checks 1326 $this->_dochecks(); 1327 //Initialization of properties 1328 $this->isunicode = $unicode; 1329 $this->page = 0; 1330 $this->transfmrk[0] = array(); 1331 $this->pagedim = array(); 1332 $this->n = 2; 1333 $this->buffer = ''; 1334 $this->pages = array(); 1335 $this->state = 0; 1336 $this->fonts = array(); 1337 $this->FontFiles = array(); 1338 $this->diffs = array(); 1339 $this->images = array(); 1340 $this->links = array(); 1341 $this->gradients = array(); 1342 $this->InFooter = false; 1343 $this->lasth = 0; 1344 $this->FontFamily = 'helvetica'; 1345 $this->FontStyle = ''; 1346 $this->FontSizePt = 12; 1347 $this->underline = false; 1348 $this->linethrough = false; 1349 $this->DrawColor = '0 G'; 1350 $this->FillColor = '0 g'; 1351 $this->TextColor = '0 g'; 1352 $this->ColorFlag = false; 1353 // encryption values 1354 $this->encrypted = false; 1355 $this->last_rc4_key = ''; 1356 $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"; 1357 //Standard Unicode fonts 1358 $this->CoreFonts = array( 1359 'courier'=>'Courier', 1360 'courierB'=>'Courier-Bold', 1361 'courierI'=>'Courier-Oblique', 1362 'courierBI'=>'Courier-BoldOblique', 1363 'helvetica'=>'Helvetica', 1364 'helveticaB'=>'Helvetica-Bold', 1365 'helveticaI'=>'Helvetica-Oblique', 1366 'helveticaBI'=>'Helvetica-BoldOblique', 1367 'times'=>'Times-Roman', 1368 'timesB'=>'Times-Bold', 1369 'timesI'=>'Times-Italic', 1370 'timesBI'=>'Times-BoldItalic', 1371 'symbol'=>'Symbol', 1372 'zapfdingbats'=>'ZapfDingbats' 1373 ); 1374 //Set scale factor 1375 $this->setPageUnit($unit); 1376 // set page format and orientation 1377 $this->setPageFormat($format, $orientation); 1378 //Page margins (1 cm) 1379 $margin = 28.35 / $this->k; 1380 $this->SetMargins($margin, $margin); 1381 //Interior cell margin 1382 $this->cMargin = $margin / 10; 1383 //Line width (0.2 mm) 1384 $this->LineWidth = 0.57 / $this->k; 1385 $this->linestyleWidth = sprintf('%.2F w', ($this->LineWidth * $this->k)); 1386 $this->linestyleCap = '0 J'; 1387 $this->linestyleJoin = '0 j'; 1388 $this->linestyleDash = '[] 0 d'; 1389 //Automatic page break 1390 $this->SetAutoPageBreak(true, (2 * $margin)); 1391 //Full width display mode 1392 $this->SetDisplayMode('fullwidth'); 1393 //Compression 1394 $this->SetCompression(true); 1395 //Set default PDF version number 1396 $this->PDFVersion = '1.7'; 1397 $this->encoding = $encoding; 1398 $this->HREF = array(); 1399 $this->getFontsList(); 1400 $this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0); 1401 $this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255); 1402 $this->extgstates = array(); 1403 // user's rights 1404 $this->sign = false; 1405 $this->ur = false; 1406 $this->ur_document = '/FullSave'; 1407 $this->ur_annots = '/Create/Delete/Modify/Copy/Import/Export'; 1408 $this->ur_form = '/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate'; 1409 $this->ur_signature = '/Modify'; 1410 // set default JPEG quality 1411 $this->jpeg_quality = 75; 1412 // initialize some settings 1413 $this->utf8Bidi(array(''), ''); 1414 // set default font 1415 $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt); 1416 // check if PCRE Unicode support is enabled 1417 if (@preg_match('/\pL/u', 'a') == 1) { 1418 // PCRE unicode support is turned ON 1419 // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator. 1420 // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants. 1421 // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between. 1422 $this->re_spaces = '/[\s\p{Z}\p{Lo}]/'; 1423 } else { 1424 // PCRE unicode support is turned OFF 1425 $this->re_spaces = '/[\s]/'; 1426 } 1427 } 1428 1429 /** 1430 * Default destructor. 1431 * @access public 1432 * @since 1.53.0.TC016 1433 */ 1434 public function __destruct() { 1435 // restore internal encoding 1436 if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) { 1437 mb_internal_encoding($this->internal_encoding); 1438 } 1439 // unset all class variables 1440 $this->_destroy(true); 1441 } 1442 1443 /** 1444 * Set the units of measure for the document. 1445 * @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. 1446 * @access public 1447 * @since 3.0.015 (2008-06-06) 1448 */ 1449 public function setPageUnit($unit) { 1450 //Set scale factor 1451 switch (strtolower($unit)) { 1452 // points 1453 case 'px': 1454 case 'pt': { 1455 $this->k = 1; 1456 break; 1457 } 1458 // millimeters 1459 case 'mm': { 1460 $this->k = $this->dpi / 25.4; 1461 break; 1462 } 1463 // centimeters 1464 case 'cm': { 1465 $this->k = $this->dpi / 2.54; 1466 break; 1467 } 1468 // inches 1469 case 'in': { 1470 $this->k = $this->dpi; 1471 break; 1472 } 1473 // unsupported unit 1474 default : { 1475 $this->Error('Incorrect unit: '.$unit); 1476 break; 1477 } 1478 } 1479 if (isset($this->CurOrientation)) { 1480 $this->setPageOrientation($this->CurOrientation); 1481 } 1482 } 1483 1484 /** 1485 * Set the page format 1486 * @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> 1487 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul> 1488 * @access public 1489 * @since 3.0.015 (2008-06-06) 1490 */ 1491 public function setPageFormat($format, $orientation='P') { 1492 //Page format 1493 if (is_string($format)) { 1494 // Page formats (45 standard ISO paper formats and 4 american common formats). 1495 // Paper cordinates are calculated in this way: (inches * 72) where (1 inch = 2.54 cm) 1496 switch (strtoupper($format)) { 1497 case '4A0': {$format = array(4767.87,6740.79); break;} 1498 case '2A0': {$format = array(3370.39,4767.87); break;} 1499 case 'A0': {$format = array(2383.94,3370.39); break;} 1500 case 'A1': {$format = array(1683.78,2383.94); break;} 1501 case 'A2': {$format = array(1190.55,1683.78); break;} 1502 case 'A3': {$format = array(841.89,1190.55); break;} 1503 case 'A4': default: {$format = array(595.28,841.89); break;} 1504 case 'A5': {$format = array(419.53,595.28); break;} 1505 case 'A6': {$format = array(297.64,419.53); break;} 1506 case 'A7': {$format = array(209.76,297.64); break;} 1507 case 'A8': {$format = array(147.40,209.76); break;} 1508 case 'A9': {$format = array(104.88,147.40); break;} 1509 case 'A10': {$format = array(73.70,104.88); break;} 1510 case 'B0': {$format = array(2834.65,4008.19); break;} 1511 case 'B1': {$format = array(2004.09,2834.65); break;} 1512 case 'B2': {$format = array(1417.32,2004.09); break;} 1513 case 'B3': {$format = array(1000.63,1417.32); break;} 1514 case 'B4': {$format = array(708.66,1000.63); break;} 1515 case 'B5': {$format = array(498.90,708.66); break;} 1516 case 'B6': {$format = array(354.33,498.90); break;} 1517 case 'B7': {$format = array(249.45,354.33); break;} 1518 case 'B8': {$format = array(175.75,249.45); break;} 1519 case 'B9': {$format = array(124.72,175.75); break;} 1520 case 'B10': {$format = array(87.87,124.72); break;} 1521 case 'C0': {$format = array(2599.37,3676.54); break;} 1522 case 'C1': {$format = array(1836.85,2599.37); break;} 1523 case 'C2': {$format = array(1298.27,1836.85); break;} 1524 case 'C3': {$format = array(918.43,1298.27); break;} 1525 case 'C4': {$format = array(649.13,918.43); break;} 1526 case 'C5': {$format = array(459.21,649.13); break;} 1527 case 'C6': {$format = array(323.15,459.21); break;} 1528 case 'C7': {$format = array(229.61,323.15); break;} 1529 case 'C8': {$format = array(161.57,229.61); break;} 1530 case 'C9': {$format = array(113.39,161.57); break;} 1531 case 'C10': {$format = array(79.37,113.39); break;} 1532 case 'RA0': {$format = array(2437.80,3458.27); break;} 1533 case 'RA1': {$format = array(1729.13,2437.80); break;} 1534 case 'RA2': {$format = array(1218.90,1729.13); break;} 1535 case 'RA3': {$format = array(864.57,1218.90); break;} 1536 case 'RA4': {$format = array(609.45,864.57); break;} 1537 case 'SRA0': {$format = array(2551.18,3628.35); break;} 1538 case 'SRA1': {$format = array(1814.17,2551.18); break;} 1539 case 'SRA2': {$format = array(1275.59,1814.17); break;} 1540 case 'SRA3': {$format = array(907.09,1275.59); break;} 1541 case 'SRA4': {$format = array(637.80,907.09); break;} 1542 case 'LETTER': {$format = array(612.00,792.00); break;} 1543 case 'LEGAL': {$format = array(612.00,1008.00); break;} 1544 case 'EXECUTIVE': {$format = array(521.86,756.00); break;} 1545 case 'FOLIO': {$format = array(612.00,936.00); break;} 1546 } 1547 $this->fwPt = $format[0]; 1548 $this->fhPt = $format[1]; 1549 } else { 1550 $this->fwPt = $format[0] * $this->k; 1551 $this->fhPt = $format[1] * $this->k; 1552 } 1553 $this->setPageOrientation($orientation); 1554 } 1555 1556 /** 1557 * Set page orientation. 1558 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul> 1559 * @param boolean $autopagebreak Boolean indicating if auto-page-break mode should be on or off. 1560 * @param float $bottommargin bottom margin of the page. 1561 * @access public 1562 * @since 3.0.015 (2008-06-06) 1563 */ 1564 public function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') { 1565 $orientation = strtoupper($orientation); 1566 if (($orientation == 'P') OR ($orientation == 'PORTRAIT')) { 1567 $this->CurOrientation = 'P'; 1568 $this->wPt = $this->fwPt; 1569 $this->hPt = $this->fhPt; 1570 } elseif (($orientation == 'L') OR ($orientation == 'LANDSCAPE')) { 1571 $this->CurOrientation = 'L'; 1572 $this->wPt = $this->fhPt; 1573 $this->hPt = $this->fwPt; 1574 } else { 1575 $this->Error('Incorrect orientation: '.$orientation); 1576 } 1577 $this->w = $this->wPt / $this->k; 1578 $this->h = $this->hPt / $this->k; 1579 if ($this->empty_string($autopagebreak)) { 1580 if (isset($this->AutoPageBreak)) { 1581 $autopagebreak = $this->AutoPageBreak; 1582 } else { 1583 $autopagebreak = true; 1584 } 1585 } 1586 if ($this->empty_string($bottommargin)) { 1587 if (isset($this->bMargin)) { 1588 $bottommargin = $this->bMargin; 1589 } else { 1590 // default value = 2 cm 1591 $bottommargin = 2 * 28.35 / $this->k; 1592 } 1593 } 1594 $this->SetAutoPageBreak($autopagebreak, $bottommargin); 1595 // store page dimensions 1596 $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); 1597 } 1598 1599 /** 1600 * Enable or disable Right-To-Left language mode 1601 * @param Boolean $enable if true enable Right-To-Left language mode. 1602 * @access public 1603 * @since 2.0.000 (2008-01-03) 1604 */ 1605 public function setRTL($enable) { 1606 $this->rtl = $enable ? true : false; 1607 $this->tmprtl = false; 1608 } 1609 1610 /** 1611 * Return the RTL status 1612 * @return boolean 1613 * @access public 1614 * @since 4.0.012 (2008-07-24) 1615 */ 1616 public function getRTL() { 1617 return $this->rtl; 1618 } 1619 1620 /** 1621 * Force temporary RTL language direction 1622 * @param mixed $mode can be false, 'L' for LTR or 'R' for RTL 1623 * @access public 1624 * @since 2.1.000 (2008-01-09) 1625 */ 1626 public function setTempRTL($mode) { 1627 switch ($mode) { 1628 case false: 1629 case 'L': 1630 case 'R': { 1631 $this->tmprtl = $mode; 1632 } 1633 } 1634 } 1635 1636 /** 1637 * Set the last cell height. 1638 * @param float $h cell height. 1639 * @author Nicola Asuni 1640 * @access public 1641 * @since 1.53.0.TC034 1642 */ 1643 public function setLastH($h) { 1644 $this->lasth = $h; 1645 } 1646 1647 /** 1648 * Get the last cell height. 1649 * @return last cell height 1650 * @access public 1651 * @since 4.0.017 (2008-08-05) 1652 */ 1653 public function getLastH() { 1654 return $this->lasth; 1655 } 1656 1657 /** 1658 * Set the adjusting factor to convert pixels to user units. 1659 * @param float $scale adjusting factor to convert pixels to user units. 1660 * @author Nicola Asuni 1661 * @access public 1662 * @since 1.5.2 1663 */ 1664 public function setImageScale($scale) { 1665 $this->imgscale = $scale; 1666 } 1667 1668 /** 1669 * Returns the adjusting factor to convert pixels to user units. 1670 * @return float adjusting factor to convert pixels to user units. 1671 * @author Nicola Asuni 1672 * @access public 1673 * @since 1.5.2 1674 */ 1675 public function getImageScale() { 1676 return $this->imgscale; 1677 } 1678 1679 /** 1680 * Returns an array of page dimensions: 1681 * <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> 1682 * @param int $pagenum page number (empty = current page) 1683 * @return array of page dimensions. 1684 * @author Nicola Asuni 1685 * @access public 1686 * @since 4.5.027 (2009-03-16) 1687 */ 1688 public function getPageDimensions($pagenum='') { 1689 if (empty($pagenum)) { 1690 $pagenum = $this->page; 1691 } 1692 return $this->pagedim[$pagenum]; 1693 } 1694 1695 /** 1696 * Returns the page width in units. 1697 * @param int $pagenum page number (empty = current page) 1698 * @return int page width. 1699 * @author Nicola Asuni 1700 * @access public 1701 * @since 1.5.2 1702 * @see getPageDimensions() 1703 */ 1704 public function getPageWidth($pagenum='') { 1705 if (empty($pagenum)) { 1706 return $this->w; 1707 } 1708 return $this->pagedim[$pagenum]['w']; 1709 } 1710 1711 /** 1712 * Returns the page height in units. 1713 * @param int $pagenum page number (empty = current page) 1714 * @return int page height. 1715 * @author Nicola Asuni 1716 * @access public 1717 * @since 1.5.2 1718 * @see getPageDimensions() 1719 */ 1720 public function getPageHeight($pagenum='') { 1721 if (empty($pagenum)) { 1722 return $this->h; 1723 } 1724 return $this->pagedim[$pagenum]['h']; 1725 } 1726 1727 /** 1728 * Returns the page break margin. 1729 * @param int $pagenum page number (empty = current page) 1730 * @return int page break margin. 1731 * @author Nicola Asuni 1732 * @access public 1733 * @since 1.5.2 1734 * @see getPageDimensions() 1735 */ 1736 public function getBreakMargin($pagenum='') { 1737 if (empty($pagenum)) { 1738 return $this->bMargin; 1739 } 1740 return $this->pagedim[$pagenum]['bm']; 1741 } 1742 1743 /** 1744 * Returns the scale factor (number of points in user unit). 1745 * @return int scale factor. 1746 * @author Nicola Asuni 1747 * @access public 1748 * @since 1.5.2 1749 */ 1750 public function getScaleFactor() { 1751 return $this->k; 1752 } 1753 1754 /** 1755 * Defines the left, top and right margins. By default, they equal 1 cm. Call this method to change them. 1756 * @param float $left Left margin. 1757 * @param float $top Top margin. 1758 * @param float $right Right margin. Default value is the left one. 1759 * @access public 1760 * @since 1.0 1761 * @see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak() 1762 */ 1763 public function SetMargins($left, $top, $right=-1) { 1764 //Set left, top and right margins 1765 $this->lMargin = $left; 1766 $this->tMargin = $top; 1767 if ($right == -1) { 1768 $right = $left; 1769 } 1770 $this->rMargin = $right; 1771 } 1772 1773 /** 1774 * 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. 1775 * @param float $margin The margin. 1776 * @access public 1777 * @since 1.4 1778 * @see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins() 1779 */ 1780 public function SetLeftMargin($margin) { 1781 //Set left margin 1782 $this->lMargin=$margin; 1783 if (($this->page > 0) AND ($this->x < $margin)) { 1784 $this->x = $margin; 1785 } 1786 } 1787 1788 /** 1789 * Defines the top margin. The method can be called before creating the first page. 1790 * @param float $margin The margin. 1791 * @access public 1792 * @since 1.5 1793 * @see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins() 1794 */ 1795 public function SetTopMargin($margin) { 1796 //Set top margin 1797 $this->tMargin=$margin; 1798 if (($this->page > 0) AND ($this->y < $margin)) { 1799 $this->y = $margin; 1800 } 1801 } 1802 1803 /** 1804 * Defines the right margin. The method can be called before creating the first page. 1805 * @param float $margin The margin. 1806 * @access public 1807 * @since 1.5 1808 * @see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins() 1809 */ 1810 public function SetRightMargin($margin) { 1811 $this->rMargin=$margin; 1812 if (($this->page > 0) AND ($this->x > ($this->w - $margin))) { 1813 $this->x = $this->w - $margin; 1814 } 1815 } 1816 1817 /** 1818 * Set the internal Cell padding. 1819 * @param float $pad internal padding. 1820 * @access public 1821 * @since 2.1.000 (2008-01-09) 1822 * @see Cell(), SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins() 1823 */ 1824 public function SetCellPadding($pad) { 1825 $this->cMargin = $pad; 1826 } 1827 1828 /** 1829 * 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. 1830 * @param boolean $auto Boolean indicating if mode should be on or off. 1831 * @param float $margin Distance from the bottom of the page. 1832 * @access public 1833 * @since 1.0 1834 * @see Cell(), MultiCell(), AcceptPageBreak() 1835 */ 1836 public function SetAutoPageBreak($auto, $margin=0) { 1837 //Set auto page break mode and triggering margin 1838 $this->AutoPageBreak = $auto; 1839 $this->bMargin = $margin; 1840 $this->PageBreakTrigger = $this->h - $margin; 1841 } 1842 1843 /** 1844 * Defines the way the document is to be displayed by the viewer. 1845 * @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> 1846 * @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> 1847 * @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> 1848 * @access public 1849 * @since 1.2 1850 */ 1851 public function SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone') { 1852 //Set display mode in viewer 1853 if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) { 1854 $this->ZoomMode = $zoom; 1855 } else { 1856 $this->Error('Incorrect zoom display mode: '.$zoom); 1857 } 1858 switch ($layout) { 1859 case 'default': 1860 case 'single': 1861 case 'SinglePage': { 1862 $this->LayoutMode = 'SinglePage'; 1863 break; 1864 } 1865 case 'continuous': 1866 case 'OneColumn': { 1867 $this->LayoutMode = 'OneColumn'; 1868 break; 1869 } 1870 case 'two': 1871 case 'TwoColumnLeft': { 1872 $this->LayoutMode = 'TwoColumnLeft'; 1873 break; 1874 } 1875 case 'TwoColumnRight': { 1876 $this->LayoutMode = 'TwoColumnRight'; 1877 break; 1878 } 1879 case 'TwoPageLeft': { 1880 $this->LayoutMode = 'TwoPageLeft'; 1881 break; 1882 } 1883 case 'TwoPageRight': { 1884 $this->LayoutMode = 'TwoPageRight'; 1885 break; 1886 } 1887 default: { 1888 $this->LayoutMode = 'SinglePage'; 1889 } 1890 } 1891 switch ($mode) { 1892 case 'UseNone': { 1893 $this->PageMode = 'UseNone'; 1894 break; 1895 } 1896 case 'UseOutlines': { 1897 $this->PageMode = 'UseOutlines'; 1898 break; 1899 } 1900 case 'UseThumbs': { 1901 $this->PageMode = 'UseThumbs'; 1902 break; 1903 } 1904 case 'FullScreen': { 1905 $this->PageMode = 'FullScreen'; 1906 break; 1907 } 1908 case 'UseOC': { 1909 $this->PageMode = 'UseOC'; 1910 break; 1911 } 1912 case '': { 1913 $this->PageMode = 'UseAttachments'; 1914 break; 1915 } 1916 default: { 1917 $this->PageMode = 'UseNone'; 1918 } 1919 } 1920 } 1921 1922 /** 1923 * 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. 1924 * Note: the Zlib extension is required for this feature. If not present, compression will be turned off. 1925 * @param boolean $compress Boolean indicating if compression must be enabled. 1926 * @access public 1927 * @since 1.4 1928 */ 1929 public function SetCompression($compress) { 1930 //Set page compression 1931 if (function_exists('gzcompress')) { 1932 $this->compress = $compress; 1933 } else { 1934 $this->compress = false; 1935 } 1936 } 1937 1938 /** 1939 * Defines the title of the document. 1940 * @param string $title The title. 1941 * @access public 1942 * @since 1.2 1943 * @see SetAuthor(), SetCreator(), SetKeywords(), SetSubject() 1944 */ 1945 public function SetTitle($title) { 1946 //Title of document 1947 $this->title = $title; 1948 } 1949 1950 /** 1951 * Defines the subject of the document. 1952 * @param string $subject The subject. 1953 * @access public 1954 * @since 1.2 1955 * @see SetAuthor(), SetCreator(), SetKeywords(), SetTitle() 1956 */ 1957 public function SetSubject($subject) { 1958 //Subject of document 1959 $this->subject = $subject; 1960 } 1961 1962 /** 1963 * Defines the author of the document. 1964 * @param string $author The name of the author. 1965 * @access public 1966 * @since 1.2 1967 * @see SetCreator(), SetKeywords(), SetSubject(), SetTitle() 1968 */ 1969 public function SetAuthor($author) { 1970 //Author of document 1971 $this->author = $author; 1972 } 1973 1974 /** 1975 * Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'. 1976 * @param string $keywords The list of keywords. 1977 * @access public 1978 * @since 1.2 1979 * @see SetAuthor(), SetCreator(), SetSubject(), SetTitle() 1980 */ 1981 public function SetKeywords($keywords) { 1982 //Keywords of document 1983 $this->keywords = $keywords; 1984 } 1985 1986 /** 1987 * Defines the creator of the document. This is typically the name of the application that generates the PDF. 1988 * @param string $creator The name of the creator. 1989 * @access public 1990 * @since 1.2 1991 * @see SetAuthor(), SetKeywords(), SetSubject(), SetTitle() 1992 */ 1993 public function SetCreator($creator) { 1994 //Creator of document 1995 $this->creator = $creator; 1996 } 1997 1998 /** 1999 * 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. 2000 * 2004-06-11 :: Nicola Asuni : changed bold tag with strong 2001 * @param string $msg The error message 2002 * @access public 2003 * @since 1.0 2004 */ 2005 public function Error($msg) { 2006 // unset all class variables 2007 $this->_destroy(true); 2008 // exit program and print error 2009 die('<strong>TCPDF ERROR: </strong>'.$msg); 2010 } 2011 2012 /** 2013 * This method begins the generation of the PDF document. 2014 * It is not necessary to call it explicitly because AddPage() does it automatically. 2015 * Note: no page is created by this method 2016 * @access public 2017 * @since 1.0 2018 * @see AddPage(), Close() 2019 */ 2020 public function Open() { 2021 //Begin document 2022 $this->state = 1; 2023 } 2024 2025 /** 2026 * Terminates the PDF document. 2027 * It is not necessary to call this method explicitly because Output() does it automatically. 2028 * If the document contains no page, AddPage() is called to prevent from getting an invalid document. 2029 * @access public 2030 * @since 1.0 2031 * @see Open(), Output() 2032 */ 2033 public function Close() { 2034 if ($this->state == 3) { 2035 return; 2036 } 2037 if ($this->page == 0) { 2038 $this->AddPage(); 2039 } 2040 // close page 2041 $this->endPage(); 2042 // close document 2043 $this->_enddoc(); 2044 // unset all class variables (except critical ones) 2045 $this->_destroy(false); 2046 } 2047 2048 /** 2049 * Move pointer at the specified document page and update page dimensions. 2050 * @param int $pnum page number 2051 * @param boolean $resetmargins if true reset left, right, top margins and Y position. 2052 * @access public 2053 * @since 2.1.000 (2008-01-07) 2054 * @see getPage(), lastpage(), getNumPages() 2055 */ 2056 public function setPage($pnum, $resetmargins=false) { 2057 if ($pnum == $this->page) { 2058 return; 2059 } 2060 if (($pnum > 0) AND ($pnum <= $this->numpages)) { 2061 $this->state = 2; 2062 // save current graphic settings 2063 //$gvars = $this->getGraphicVars(); 2064 $oldpage = $this->page; 2065 $this->page = $pnum; 2066 $this->wPt = $this->pagedim[$this->page]['w']; 2067 $this->hPt = $this->pagedim[$this->page]['h']; 2068 $this->w = $this->wPt / $this->k; 2069 $this->h = $this->hPt / $this->k; 2070 $this->tMargin = $this->pagedim[$this->page]['tm']; 2071 $this->bMargin = $this->pagedim[$this->page]['bm']; 2072 $this->original_lMargin = $this->pagedim[$this->page]['olm']; 2073 $this->original_rMargin = $this->pagedim[$this->page]['orm']; 2074 $this->AutoPageBreak = $this->pagedim[$this->page]['pb']; 2075 $this->CurOrientation = $this->pagedim[$this->page]['or']; 2076 $this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin); 2077 // restore graphic settings 2078 //$this->setGraphicVars($gvars); 2079 if ($resetmargins) { 2080 $this->lMargin = $this->pagedim[$this->page]['olm']; 2081 $this->rMargin = $this->pagedim[$this->page]['orm']; 2082 $this->SetY($this->tMargin); 2083 } else { 2084 // account for booklet mode 2085 if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) { 2086 $deltam = $this->pagedim[$this->page]['olm'] - $this->pagedim[$this->page]['orm']; 2087 $this->lMargin += $deltam; 2088 $this->rMargin -= $deltam; 2089 } 2090 } 2091 } else { 2092 $this->Error('Wrong page number on setPage() function.'); 2093 } 2094 } 2095 2096 /** 2097 * Reset pointer to the last document page. 2098 * @param boolean $resetmargins if true reset left, right, top margins and Y position. 2099 * @access public 2100 * @since 2.0.000 (2008-01-04) 2101 * @see setPage(), getPage(), getNumPages() 2102 */ 2103 public function lastPage($resetmargins=false) { 2104 $this->setPage($this->getNumPages(), $resetmargins); 2105 } 2106 2107 /** 2108 * Get current document page number. 2109 * @return int page number 2110 * @access public 2111 * @since 2.1.000 (2008-01-07) 2112 * @see setPage(), lastpage(), getNumPages() 2113 */ 2114 public function getPage() { 2115 return $this->page; 2116 } 2117 2118 2119 /** 2120 * Get the total number of insered pages. 2121 * @return int number of pages 2122 * @access public 2123 * @since 2.1.000 (2008-01-07) 2124 * @see setPage(), getPage(), lastpage() 2125 */ 2126 public function getNumPages() { 2127 return $this->numpages; 2128 } 2129 2130 /** 2131 * 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). 2132 * The origin of the coordinate system is at the top-left corner (or top-right for RTL) and increasing ordinates go downwards. 2133 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul> 2134 * @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> 2135 * @access public 2136 * @since 1.0 2137 * @see startPage(), endPage() 2138 */ 2139 public function AddPage($orientation='', $format='') { 2140 if (!isset($this->original_lMargin)) { 2141 $this->original_lMargin = $this->lMargin; 2142 } 2143 if (!isset($this->original_rMargin)) { 2144 $this->original_rMargin = $this->rMargin; 2145 } 2146 // terminate previous page 2147 $this->endPage(); 2148 // start new page 2149 $this->startPage($orientation, $format); 2150 } 2151 2152 /** 2153 * Terminate the current page 2154 * @access protected 2155 * @since 4.2.010 (2008-11-14) 2156 * @see startPage(), AddPage() 2157 */ 2158 protected function endPage() { 2159 // check if page is already closed 2160 if (($this->page == 0) OR ($this->numpages > $this->page) OR (!$this->pageopen[$this->page])) { 2161 return; 2162 } 2163 $this->InFooter = true; 2164 // print page footer 2165 $this->setFooter(); 2166 // close page 2167 $this->_endpage(); 2168 // mark page as closed 2169 $this->pageopen[$this->page] = false; 2170 $this->InFooter = false; 2171 } 2172 2173 /** 2174 * Starts a new page to the document. The page must be closed using the endPage() function. 2175 * The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards. 2176 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul> 2177 * @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> 2178 * @access protected 2179 * @since 4.2.010 (2008-11-14) 2180 * @see endPage(), AddPage() 2181 */ 2182 protected function startPage($orientation='', $format='') { 2183 if ($this->numpages > $this->page) { 2184 // this page has been already added 2185 $this->setPage($this->page + 1); 2186 $this->SetY($this->tMargin); 2187 return; 2188 } 2189 // start a new page 2190 if ($this->state == 0) { 2191 $this->Open(); 2192 } 2193 ++$this->numpages; 2194 $this->swapMargins($this->booklet); 2195 // save current graphic settings 2196 $gvars = $this->getGraphicVars(); 2197 // start new page 2198 $this->_beginpage($orientation, $format); 2199 // mark page as open 2200 $this->pageopen[$this->page] = true; 2201 // restore graphic settings 2202 $this->setGraphicVars($gvars); 2203 // mark this point 2204 $this->setPageMark(); 2205 // print page header 2206 $this->setHeader(); 2207 // restore graphic settings 2208 $this->setGraphicVars($gvars); 2209 // mark this point 2210 $this->setPageMark(); 2211 // print table header (if any) 2212 $this->setTableHeader(); 2213 } 2214 2215 /** 2216 * Set start-writing mark on current page for multicell borders and fills. 2217 * This function must be called after calling Image() function for a background image. 2218 * Background images must be always inserted before calling Multicell() or WriteHTMLCell() or WriteHTML() functions. 2219 * @access public 2220 * @since 4.0.016 (2008-07-30) 2221 */ 2222 public function setPageMark() { 2223 $this->intmrk[$this->page] = $this->pagelen[$this->page]; 2224 } 2225 2226 /** 2227 * Set header data. 2228 * @param string $ln header image logo 2229 * @param string $lw header image logo width in mm 2230 * @param string $ht string to print as title on document header 2231 * @param string $hs string to print on document header 2232 * @access public 2233 */ 2234 public function setHeaderData($ln='', $lw=0, $ht='', $hs='') { 2235 $this->header_logo = $ln; 2236 $this->header_logo_width = $lw; 2237 $this->header_title = $ht; 2238 $this->header_string = $hs; 2239 } 2240 2241 /** 2242 * Returns header data: 2243 * <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> 2244 * @return array() 2245 * @access public 2246 * @since 4.0.012 (2008-07-24) 2247 */ 2248 public function getHeaderData() { 2249 $ret = array(); 2250 $ret['logo'] = $this->header_logo; 2251 $ret['logo_width'] = $this->header_logo_width; 2252 $ret['title'] = $this->header_title; 2253 $ret['string'] = $this->header_string; 2254 return $ret; 2255 } 2256 2257 /** 2258 * Set header margin. 2259 * (minimum distance between header and top page margin) 2260 * @param int $hm distance in user units 2261 * @access public 2262 */ 2263 public function setHeaderMargin($hm=10) { 2264 $this->header_margin = $hm; 2265 } 2266 2267 /** 2268 * Returns header margin in user units. 2269 * @return float 2270 * @since 4.0.012 (2008-07-24) 2271 * @access public 2272 */ 2273 public function getHeaderMargin() { 2274 return $this->header_margin; 2275 } 2276 2277 /** 2278 * Set footer margin. 2279 * (minimum distance between footer and bottom page margin) 2280 * @param int $fm distance in user units 2281 * @access public 2282 */ 2283 public function setFooterMargin($fm=10) { 2284 $this->footer_margin = $fm; 2285 } 2286 2287 /** 2288 * Returns footer margin in user units. 2289 * @return float 2290 * @since 4.0.012 (2008-07-24) 2291 * @access public 2292 */ 2293 public function getFooterMargin() { 2294 return $this->footer_margin; 2295 } 2296 /** 2297 * Set a flag to print page header. 2298 * @param boolean $val set to true to print the page header (default), false otherwise. 2299 * @access public 2300 */ 2301 public function setPrintHeader($val=true) { 2302 $this->print_header = $val; 2303 } 2304 2305 /** 2306 * Set a flag to print page footer. 2307 * @param boolean $value set to true to print the page footer (default), false otherwise. 2308 * @access public 2309 */ 2310 public function setPrintFooter($val=true) { 2311 $this->print_footer = $val; 2312 } 2313 2314 /** 2315 * Return the right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image 2316 * @return float 2317 * @access public 2318 */ 2319 public function getImageRBX() { 2320 return $this->img_rb_x; 2321 } 2322 2323 /** 2324 * Return the right-bottom (or left-bottom for RTL) corner Y coordinate of last inserted image 2325 * @return float 2326 * @access public 2327 */ 2328 public function getImageRBY() { 2329 return $this->img_rb_y; 2330 } 2331 2332 /** 2333 * This method is used to render the page header. 2334 * It is automatically called by AddPage() and could be overwritten in your own inherited class. 2335 * @access public 2336 */ 2337 public function Header() { 2338 $ormargins = $this->getOriginalMargins(); 2339 $headerfont = $this->getHeaderFont(); 2340 $headerdata = $this->getHeaderData(); 2341 if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) { 2342 $this->Image(K_PATH_IMAGES.$headerdata['logo'], $this->GetX(), $this->getHeaderMargin(), $headerdata['logo_width']); 2343 $imgy = $this->getImageRBY(); 2344 } else { 2345 $imgy = $this->GetY(); 2346 } 2347 $cell_height = round(($this->getCellHeightRatio() * $headerfont[2]) / $this->getScaleFactor(), 2); 2348 // set starting margin for text data cell 2349 if ($this->getRTL()) { 2350 $header_x = $ormargins['right'] + ($headerdata['logo_width'] * 1.1); 2351 } else { 2352 $header_x = $ormargins['left'] + ($headerdata['logo_width'] * 1.1); 2353 } 2354 $this->SetTextColor(0, 0, 0); 2355 // header title 2356 $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1); 2357 $this->SetX($header_x); 2358 $this->Cell(0, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0); 2359 // header string 2360 $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]); 2361 $this->SetX($header_x); 2362 $this->MultiCell(0, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false); 2363 // print an ending header line 2364 $this->SetLineStyle(array('width' => 0.85 / $this->getScaleFactor(), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))); 2365 $this->SetY((2.835 / $this->getScaleFactor()) + max($imgy, $this->GetY())); 2366 if ($this->getRTL()) { 2367 $this->SetX($ormargins['right']); 2368 } else { 2369 $this->SetX($ormargins['left']); 2370 } 2371 $this->Cell(0, 0, '', 'T', 0, 'C'); 2372 } 2373 2374 /** 2375 * This method is used to render the page footer. 2376 * It is automatically called by AddPage() and could be overwritten in your own inherited class. 2377 * @access public 2378 */ 2379 public function Footer() { 2380 $cur_y = $this->GetY(); 2381 $ormargins = $this->getOriginalMargins(); 2382 $this->SetTextColor(0, 0, 0); 2383 //set style for cell border 2384 $line_width = 0.85 / $this->getScaleFactor(); 2385 $this->SetLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))); 2386 //print document barcode 2387 $barcode = $this->getBarcode(); 2388 if (!empty($barcode)) { 2389 $this->Ln($line_width); 2390 $barcode_width = round(($this->getPageWidth() - $ormargins['left'] - $ormargins['right'])/3); 2391 $this->write1DBarcode($barcode, 'C128B', $this->GetX(), $cur_y + $line_width, $barcode_width, (($this->getFooterMargin() / 3) - $line_width), 0.3, '', ''); 2392 } 2393 if (empty($this->pagegroups)) { 2394 $pagenumtxt = $this->l['w_page'].' '.$this->getAliasNumPage().' / '.$this->getAliasNbPages(); 2395 } else { 2396 $pagenumtxt = $this->l['w_page'].' '.$this->getPageNumGroupAlias().' / '.$this->getPageGroupAlias(); 2397 } 2398 $this->SetY($cur_y); 2399 //Print page number 2400 if ($this->getRTL()) { 2401 $this->SetX($ormargins['right']); 2402 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L'); 2403 } else { 2404 $this->SetX($ormargins['left']); 2405 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'R'); 2406 } 2407 } 2408 2409 /** 2410 * This method is used to render the page header. 2411 * @access protected 2412 * @since 4.0.012 (2008-07-24) 2413 */ 2414 protected function setHeader() { 2415 if ($this->print_header) { 2416 $lasth = $this->lasth; 2417 $this->_out('q'); 2418 $this->rMargin = $this->original_rMargin; 2419 $this->lMargin = $this->original_lMargin; 2420 $this->cMargin = 0; 2421 //set current position 2422 if ($this->rtl) { 2423 $this->SetXY($this->original_rMargin, $this->header_margin); 2424 } else { 2425 $this->SetXY($this->original_lMargin, $this->header_margin); 2426 } 2427 $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]); 2428 $this->Header(); 2429 //restore position 2430 if ($this->rtl) { 2431 $this->SetXY($this->original_rMargin, $this->tMargin); 2432 } else { 2433 $this->SetXY($this->original_lMargin, $this->tMargin); 2434 } 2435 $this->_out('Q'); 2436 $this->lasth = $lasth; 2437 } 2438 } 2439 2440 /** 2441 * This method is used to render the page footer. 2442 * @access protected 2443 * @since 4.0.012 (2008-07-24) 2444 */ 2445 protected function setFooter() { 2446 //Page footer 2447 // save current graphic settings 2448 $gvars = $this->getGraphicVars(); 2449 // mark this point 2450 $this->footerpos[$this->page] = $this->pagelen[$this->page]; 2451 $this->_out("\n"); 2452 if ($this->print_footer) { 2453 $lasth = $this->lasth; 2454 $this->_out('q'); 2455 $this->rMargin = $this->original_rMargin; 2456 $this->lMargin = $this->original_lMargin; 2457 $this->cMargin = 0; 2458 //set current position 2459 $footer_y = $this->h - $this->footer_margin; 2460 if ($this->rtl) { 2461 $this->SetXY($this->original_rMargin, $footer_y); 2462 } else { 2463 $this->SetXY($this->original_lMargin, $footer_y); 2464 } 2465 $this->SetFont($this->footer_font[0], $this->footer_font[1], $this->footer_font[2]); 2466 $this->Footer(); 2467 //restore position 2468 if ($this->rtl) { 2469 $this->SetXY($this->original_rMargin, $this->tMargin); 2470 } else { 2471 $this->SetXY($this->original_lMargin, $this->tMargin); 2472 } 2473 $this->_out('Q'); 2474 $this->lasth = $lasth; 2475 } 2476 // restore graphic settings 2477 $this->setGraphicVars($gvars); 2478 // calculate footer lenght 2479 $this->footerlen[$this->page] = $this->pagelen[$this->page] - $this->footerpos[$this->page] + 1; 2480 } 2481 2482 /** 2483 * This method is used to render the table header on new page (if any). 2484 * @access protected 2485 * @since 4.5.030 (2009-03-25) 2486 */ 2487 protected function setTableHeader() { 2488 if (!$this->empty_string($this->theadMargin)) { 2489 // restore the original top-margin 2490 $this->tMargin = $this->theadMargin; 2491 $this->pagedim[$this->page]['tm'] = $this->theadMargin; 2492 $this->y = $this->theadMargin; 2493 } 2494 if (!$this->empty_string($this->thead)) { 2495 // print table header 2496 $this->writeHTML($this->thead, false, false, false, false, ''); 2497 // set new top margin to skip the table headers 2498 if (!isset($this->theadMargin) OR ($this->empty_string($this->theadMargin))) { 2499 $this->theadMargin = $this->tMargin; 2500 } 2501 $this->tMargin = $this->y; 2502 $this->pagedim[$this->page]['tm'] = $this->tMargin; 2503 } 2504 } 2505 2506 /** 2507 * Returns the current page number. 2508 * @return int page number 2509 * @access public 2510 * @since 1.0 2511 * @see AliasNbPages(), getAliasNbPages() 2512 */ 2513 public function PageNo() { 2514 return $this->page; 2515 } 2516 2517 /** 2518 * Defines a new spot color. 2519 * It can be expressed in RGB components or gray scale. 2520 * The method can be called before the first page is created and the value is retained from page to page. 2521 * @param int $c Cyan color for CMYK. Value between 0 and 255 2522 * @param int $m Magenta color for CMYK. Value between 0 and 255 2523 * @param int $y Yellow color for CMYK. Value between 0 and 255 2524 * @param int $k Key (Black) color for CMYK. Value between 0 and 255 2525 * @access public 2526 * @since 4.0.024 (2008-09-12) 2527 * @see SetDrawSpotColor(), SetFillSpotColor(), SetTextSpotColor() 2528 */ 2529 public function AddSpotColor($name, $c, $m, $y, $k) { 2530 if (!isset($this->spot_colors[$name])) { 2531 $i = 1 + count($this->spot_colors); 2532 $this->spot_colors[$name] = array('i' => $i, 'c' => $c, 'm' => $m, 'y' => $y, 'k' => $k); 2533 } 2534 } 2535 2536 /** 2537 * Defines the color used for all drawing operations (lines, rectangles and cell borders). 2538 * It can be expressed in RGB components or gray scale. 2539 * The method can be called before the first page is created and the value is retained from page to page. 2540 * @param array $color array of colors 2541 * @access public 2542 * @since 3.1.000 (2008-06-11) 2543 * @see SetDrawColor() 2544 */ 2545 public function SetDrawColorArray($color) { 2546 if (isset($color)) { 2547 $color = array_values($color); 2548 $r = isset($color[0]) ? $color[0] : -1; 2549 $g = isset($color[1]) ? $color[1] : -1; 2550 $b = isset($color[2]) ? $color[2] : -1; 2551 $k = isset($color[3]) ? $color[3] : -1; 2552 if ($r >= 0) { 2553 $this->SetDrawColor($r, $g, $b, $k); 2554 } 2555 } 2556 } 2557 2558 /** 2559 * 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. 2560 * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255 2561 * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255 2562 * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255 2563 * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255 2564 * @access public 2565 * @since 1.3 2566 * @see SetDrawColorArray(), SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell() 2567 */ 2568 public function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1) { 2569 // set default values 2570 if (!is_numeric($col1)) { 2571 $col1 = 0; 2572 } 2573 if (!is_numeric($col2)) { 2574 $col2 = -1; 2575 } 2576 if (!is_numeric($col3)) { 2577 $col3 = -1; 2578 } 2579 if (!is_numeric($col4)) { 2580 $col4 = -1; 2581 } 2582 //Set color for all stroking operations 2583 if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) { 2584 // Grey scale 2585 $this->DrawColor = sprintf('%.3F G', $col1/255); 2586 } elseif ($col4 == -1) { 2587 // RGB 2588 $this->DrawColor = sprintf('%.3F %.3F %.3F RG', $col1/255, $col2/255, $col3/255); 2589 } else { 2590 // CMYK 2591 $this->DrawColor = sprintf('%.3F %.3F %.3F %.3F K', $col1/100, $col2/100, $col3/100, $col4/100); 2592 } 2593 if ($this->page > 0) { 2594 $this->_out($this->DrawColor); 2595 } 2596 } 2597 2598 /** 2599 * Defines the spot color used for all drawing operations (lines, rectangles and cell borders). 2600 * @param string $name name of the spot color 2601 * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default). 2602 * @access public 2603 * @since 4.0.024 (2008-09-12) 2604 * @see AddSpotColor(), SetFillSpotColor(), SetTextSpotColor() 2605 */ 2606 public function SetDrawSpotColor($name, $tint=100) { 2607 if (!isset($this->spot_colors[$name])) { 2608 $this->Error('Undefined spot color: '.$name); 2609 } 2610 $this->DrawColor = sprintf('/CS%d CS %.3F SCN', $this->spot_colors[$name]['i'], $tint/100); 2611 if ($this->page > 0) { 2612 $this->_out($this->DrawColor); 2613 } 2614 } 2615 2616 /** 2617 * Defines the color used for all filling operations (filled rectangles and cell backgrounds). 2618 * It can be expressed in RGB components or gray scale. 2619 * The method can be called before the first page is created and the value is retained from page to page. 2620 * @param array $color array of colors 2621 * @access public 2622 * @since 3.1.000 (2008-6-11) 2623 * @see SetFillColor() 2624 */ 2625 public function SetFillColorArray($color) { 2626 if (isset($color)) { 2627 $color = array_values($color); 2628 $r = isset($color[0]) ? $color[0] : -1; 2629 $g = isset($color[1]) ? $color[1] : -1; 2630 $b = isset($color[2]) ? $color[2] : -1; 2631 $k = isset($color[3]) ? $color[3] : -1; 2632 if ($r >= 0) { 2633 $this->SetFillColor($r, $g, $b, $k); 2634 } 2635 } 2636 } 2637 2638 /** 2639 * 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. 2640 * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255 2641 * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255 2642 * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255 2643 * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255 2644 * @access public 2645 * @since 1.3 2646 * @see SetFillColorArray(), SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell() 2647 */ 2648 public function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1) { 2649 // set default values 2650 if (!is_numeric($col1)) { 2651 $col1 = 0; 2652 } 2653 if (!is_numeric($col2)) { 2654 $col2 = -1; 2655 } 2656 if (!is_numeric($col3)) { 2657 $col3 = -1; 2658 } 2659 if (!is_numeric($col4)) { 2660 $col4 = -1; 2661 } 2662 //Set color for all filling operations 2663 if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) { 2664 // Grey scale 2665 $this->FillColor = sprintf('%.3F g', $col1/255); 2666 $this->bgcolor = array('G' => $col1); 2667 } elseif ($col4 == -1) { 2668 // RGB 2669 $this->FillColor = sprintf('%.3F %.3F %.3F rg', $col1/255, $col2/255, $col3/255); 2670 $this->bgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3); 2671 } else { 2672 // CMYK 2673 $this->FillColor = sprintf('%.3F %.3F %.3F %.3F k', $col1/100, $col2/100, $col3/100, $col4/100); 2674 $this->bgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4); 2675 } 2676 $this->ColorFlag = ($this->FillColor != $this->TextColor); 2677 if ($this->page > 0) { 2678 $this->_out($this->FillColor); 2679 } 2680 } 2681 2682 /** 2683 * Defines the spot color used for all filling operations (filled rectangles and cell backgrounds). 2684 * @param string $name name of the spot color 2685 * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default). 2686 * @access public 2687 * @since 4.0.024 (2008-09-12) 2688 * @see AddSpotColor(), SetDrawSpotColor(), SetTextSpotColor() 2689 */ 2690 public function SetFillSpotColor($name, $tint=100) { 2691 if (!isset($this->spot_colors[$name])) { 2692 $this->Error('Undefined spot color: '.$name); 2693 } 2694 $this->FillColor = sprintf('/CS%d cs %.3F scn', $this->spot_colors[$name]['i'], $tint/100); 2695 $this->ColorFlag = ($this->FillColor != $this->TextColor); 2696 if ($this->page > 0) { 2697 $this->_out($this->FillColor); 2698 } 2699 } 2700 2701 /** 2702 * Defines the color used for text. It can be expressed in RGB components or gray scale. 2703 * The method can be called before the first page is created and the value is retained from page to page. 2704 * @param array $color array of colors 2705 * @access public 2706 * @since 3.1.000 (2008-6-11) 2707 * @see SetFillColor() 2708 */ 2709 public function SetTextColorArray($color) { 2710 if (isset($color)) { 2711 $color = array_values($color); 2712 $r = isset($color[0]) ? $color[0] : -1; 2713 $g = isset($color[1]) ? $color[1] : -1; 2714 $b = isset($color[2]) ? $color[2] : -1; 2715 $k = isset($color[3]) ? $color[3] : -1; 2716 if ($r >= 0) { 2717 $this->SetTextColor($r, $g, $b, $k); 2718 } 2719 } 2720 } 2721 2722 /** 2723 * 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. 2724 * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255 2725 * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255 2726 * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255 2727 * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255 2728 * @access public 2729 * @since 1.3 2730 * @see SetTextColorArray(), SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell() 2731 */ 2732 public function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1) { 2733 // set default values 2734 if (!is_numeric($col1)) { 2735 $col1 = 0; 2736 } 2737 if (!is_numeric($col2)) { 2738 $col2 = -1; 2739 } 2740 if (!is_numeric($col3)) { 2741 $col3 = -1; 2742 } 2743 if (!is_numeric($col4)) { 2744 $col4 = -1; 2745 } 2746 //Set color for text 2747 if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) { 2748 // Grey scale 2749 $this->TextColor = sprintf('%.3F g', $col1/255); 2750 $this->fgcolor = array('G' => $col1); 2751 } elseif ($col4 == -1) { 2752 // RGB 2753 $this->TextColor = sprintf('%.3F %.3F %.3F rg', $col1/255, $col2/255, $col3/255); 2754 $this->fgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3); 2755 } else { 2756 // CMYK 2757 $this->TextColor = sprintf('%.3F %.3F %.3F %.3F k', $col1/100, $col2/100, $col3/100, $col4/100); 2758 $this->fgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4); 2759 } 2760 $this->ColorFlag = ($this->FillColor != $this->TextColor); 2761 } 2762 2763 /** 2764 * Defines the spot color used for text. 2765 * @param string $name name of the spot color 2766 * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default). 2767 * @access public 2768 * @since 4.0.024 (2008-09-12) 2769 * @see AddSpotColor(), SetDrawSpotColor(), SetFillSpotColor() 2770 */ 2771 public function SetTextSpotColor($name, $tint=100) { 2772 if (!isset($this->spot_colors[$name])) { 2773 $this->Error('Undefined spot color: '.$name); 2774 } 2775 $this->TextColor = sprintf('/CS%d cs %.3F scn', $this->spot_colors[$name]['i'], $tint/100); 2776 $this->ColorFlag = ($this->FillColor != $this->TextColor); 2777 if ($this->page > 0) { 2778 $this->_out($this->TextColor); 2779 } 2780 } 2781 2782 /** 2783 * Returns the length of a string in user unit. A font must be selected.<br> 2784 * @param string $s The string whose length is to be computed 2785 * @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. 2786 * @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></ul> or any combination. The default value is regular. 2787 * @param float $fontsize Font size in points. The default value is the current size. 2788 * @return int string length 2789 * @author Nicola Asuni 2790 * @access public 2791 * @since 1.2 2792 */ 2793 public function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0) { 2794 return $this->GetArrStringWidth($this->utf8Bidi($this->UTF8StringToArray($s), $s, $this->tmprtl), $fontname, $fontstyle, $fontsize); 2795 } 2796 2797 /** 2798 * Returns the string length of an array of chars in user unit. A font must be selected.<br> 2799 * @param string $arr The array of chars whose total length is to be computed 2800 * @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. 2801 * @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></ul> or any combination. The default value is regular. 2802 * @param float $fontsize Font size in points. The default value is the current size. 2803 * @return int string length 2804 * @author Nicola Asuni 2805 * @access public 2806 * @since 2.4.000 (2008-03-06) 2807 */ 2808 public function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0) { 2809 // store current values 2810 if (!$this->empty_string($fontname)) { 2811 $prev_FontFamily = $this->FontFamily; 2812 $prev_FontStyle = $this->FontStyle; 2813 $prev_FontSizePt = $this->FontSizePt; 2814 $this->SetFont($fontname, $fontstyle, $fontsize); 2815 } 2816 $w = 0; 2817 foreach ($sa as $char) { 2818 $w += $this->GetCharWidth($char); 2819 } 2820 // restore previous values 2821 if (!$this->empty_string($fontname)) { 2822 $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt); 2823 } 2824 return $w; 2825 } 2826 2827 /** 2828 * Returns the length of the char in user unit for the current font.<br> 2829 * @param int $char The char code whose length is to be returned 2830 * @return int char width 2831 * @author Nicola Asuni 2832 * @access public 2833 * @since 2.4.000 (2008-03-06) 2834 */ 2835 public function GetCharWidth($char) { 2836 if ($char == 173) { 2837 // SHY character will not be printed 2838 return (0); 2839 } 2840 $cw = &$this->CurrentFont['cw']; 2841 if (isset($cw[$char])) { 2842 $w = $cw[$char]; 2843 } elseif (isset($this->CurrentFont['dw'])) { 2844 // default width 2845 $w = $this->CurrentFont['dw']; 2846 } elseif (isset($cw[32])) { 2847 // default width 2848 $dw = $cw[32]; 2849 } else { 2850 $w = 600; 2851 } 2852 return ($w * $this->FontSize / 1000); 2853 } 2854 2855 /** 2856 * Returns the numbero of characters in a string. 2857 * @param string $s The input string. 2858 * @return int number of characters 2859 * @access public 2860 * @since 2.0.0001 (2008-01-07) 2861 */ 2862 public function GetNumChars($s) { 2863 if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) { 2864 return count($this->UTF8StringToArray($s)); 2865 } 2866 return strlen($s); 2867 } 2868 2869 /** 2870 * Fill the list of available fonts ($this->fontlist). 2871 * @access protected 2872 * @since 4.0.013 (2008-07-28) 2873 */ 2874 protected function getFontsList() { 2875 $fontsdir = opendir($this->_getfontpath()); 2876 while (($file = readdir($fontsdir)) !== false) { 2877 if (substr($file, -4) == '.php') { 2878 array_push($this->fontlist, strtolower(basename($file, '.php'))); 2879 } 2880 } 2881 closedir($fontsdir); 2882 } 2883 2884 /** 2885 * Imports a TrueType, Type1, core, or CID0 font and makes it available. 2886 * It is necessary to generate a font definition file first (read /fonts/utils/README.TXT). 2887 * 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. 2888 * @param string $family Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font. 2889 * @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> 2890 * @param string $fontfile The font definition file. By default, the name is built from the family and style, in lower case with no spaces. 2891 * @return array containing the font data, or false in case of error. 2892 * @access public 2893 * @since 1.5 2894 * @see SetFont() 2895 */ 2896 public function AddFont($family, $style='', $fontfile='') { 2897 if ($this->empty_string($family)) { 2898 if (!$this->empty_string($this->FontFamily)) { 2899 $family = $this->FontFamily; 2900 } else { 2901 $this->Error('Empty font family'); 2902 } 2903 } 2904 $family = strtolower($family); 2905 if ((!$this->isunicode) AND ($family == 'arial')) { 2906 $family = 'helvetica'; 2907 } 2908 if (($family == 'symbol') OR ($family == 'zapfdingbats')) { 2909 $style = ''; 2910 } 2911 $tempstyle = strtoupper($style); 2912 $style = ''; 2913 // underline 2914 if (strpos($tempstyle, 'U') !== false) { 2915 $this->underline = true; 2916 } else { 2917 $this->underline = false; 2918 } 2919 // line through (deleted) 2920 if (strpos($tempstyle, 'D') !== false) { 2921 $this->linethrough = true; 2922 } else { 2923 $this->linethrough = false; 2924 } 2925 // bold 2926 if (strpos($tempstyle, 'B') !== false) { 2927 $style .= 'B'; 2928 } 2929 // oblique 2930 if (strpos($tempstyle, 'I') !== false) { 2931 $style .= 'I'; 2932 } 2933 $bistyle = $style; 2934 $fontkey = $family.$style; 2935 $font_style = $style.($this->underline ? 'U' : '').($this->linethrough ? 'D' : ''); 2936 $fontdata = array('fontkey' => $fontkey, 'family' => $family, 'style' => $font_style); 2937 // check if the font has been already added 2938 if ($this->getFontBuffer($fontkey) !== false) { 2939 return $fontdata; 2940 } 2941 if (isset($type)) { 2942 unset($type); 2943 } 2944 if (isset($cw)) { 2945 unset($cw); 2946 } 2947 // get specified font directory (if any) 2948 $fontdir = ''; 2949 if (!$this->empty_string($fontfile)) { 2950 $fontdir = dirname($fontfile); 2951 if ($this->empty_string($fontdir) OR ($fontdir == '.')) { 2952 $fontdir = ''; 2953 } else { 2954 $fontdir .= '/'; 2955 } 2956 } 2957 // search and include font file 2958 if ($this->empty_string($fontfile) OR (!file_exists($fontfile))) { 2959 // build a standard filenames for specified font 2960 $fontfile1 = str_replace(' ', '', $family).strtolower($style).'.php'; 2961 $fontfile2 = str_replace(' ', '', $family).'.php'; 2962 // search files on various directories 2963 if (file_exists($fontdir.$fontfile1)) { 2964 $fontfile = $fontdir.$fontfile1; 2965 } elseif (file_exists($this->_getfontpath().$fontfile1)) { 2966 $fontfile = $this->_getfontpath().$fontfile1; 2967 } elseif (file_exists($fontfile1)) { 2968 $fontfile = $fontfile1; 2969 } elseif (file_exists($fontdir.$fontfile2)) { 2970 $fontfile = $fontdir.$fontfile2; 2971 } elseif (file_exists($this->_getfontpath().$fontfile2)) { 2972 $fontfile = $this->_getfontpath().$fontfile2; 2973 } else { 2974 $fontfile = $fontfile2; 2975 } 2976 } 2977 // include font file 2978 if (file_exists($fontfile)) { 2979 include($fontfile); 2980 } else { 2981 $this->Error('Could not include font definition file: '.$family.''); 2982 } 2983 // check font parameters 2984 if ((!isset($type)) OR (!isset($cw))) { 2985 $this->Error('The font definition file has a bad format: '.$fontfile.''); 2986 } 2987 if (!isset($file)) { 2988 $file = ''; 2989 } 2990 if (!isset($enc)) { 2991 $enc = ''; 2992 } 2993 if (!isset($dw) OR $this->empty_string($dw)) { 2994 // set default width 2995 if (isset($desc['MissingWidth']) AND ($desc['MissingWidth'] > 0)) { 2996 $dw = $desc['MissingWidth']; 2997 } elseif (isset($cw[32])) { 2998 $dw = $cw[32]; 2999 } else { 3000 $dw = 600; 3001 } 3002 } 3003 ++$this->numfonts; 3004 // register CID font (all styles at once) 3005 if ($type == 'cidfont0') { 3006 $file = ''; // not embedded 3007 $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic'); 3008 $sname = $name.$styles[$bistyle]; 3009 if ((strpos($bistyle, 'B') !== false) AND (isset($desc['StemV'])) AND ($desc['StemV'] == 70)) { 3010 $desc['StemV'] = 120; 3011 } 3012 $this->setFontBuffer($fontkey, array('i' => $this->numfonts, 'type' => $type, 'name' => $sname, 'desc' => $desc, 'cidinfo' => $cidinfo, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'dw' => $dw, 'enc' => $enc)); 3013 } elseif ($type == 'core') { 3014 $this->setFontBuffer($fontkey, array('i' => $this->numfonts, 'type' => 'core', 'name' => $this->CoreFonts[$fontkey], 'up' => -100, 'ut' => 50, 'cw' => $cw, 'dw' => $dw)); 3015 } elseif (($type == 'TrueType') OR ($type == 'Type1')) { 3016 $this->setFontBuffer($fontkey, array('i' => $this->numfonts, 'type' => $type, 'name' => $name, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'dw' => $dw, 'file' => $file, 'enc' => $enc, 'desc' => $desc)); 3017 } elseif ($type == 'TrueTypeUnicode') { 3018 $this->setFontBuffer($fontkey, array('i' => $this->numfonts, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'dw' => $dw, 'enc' => $enc, 'file' => $file, 'ctg' => $ctg)); 3019 } else { 3020 $this->Error('Unknow font type: '.$type.''); 3021 } 3022 if (isset($diff) AND (!empty($diff))) { 3023 //Search existing encodings 3024 $d = 0; 3025 $nb = count($this->diffs); 3026 for ($i=1; $i <= $nb; ++$i) { 3027 if ($this->diffs[$i] == $diff) { 3028 $d = $i; 3029 break; 3030 } 3031 } 3032 if ($d == 0) { 3033 $d = $nb + 1; 3034 $this->diffs[$d] = $diff; 3035 } 3036 $this->setFontSubBuffer($fontkey, 'diff', $d); 3037 } 3038 if (!$this->empty_string($file)) { 3039 if ((strcasecmp($type,'TrueType') == 0) OR (strcasecmp($type, 'TrueTypeUnicode') == 0)) { 3040 $this->FontFiles[$file] = array('length1' => $originalsize, 'fontdir' => $fontdir); 3041 } elseif ($type != 'core') { 3042 $this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2, 'fontdir' => $fontdir); 3043 } 3044 } 3045 return $fontdata; 3046 } 3047 3048 /** 3049 * Sets the font used to print character strings. 3050 * The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe). 3051 * The method can be called before the first page is created and the font is retained from page to page. 3052 * If you just wish to change the current font size, it is simpler to call SetFontSize(). 3053 * 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 /> 3054 * @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. 3055 * @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></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. 3056 * @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 3057 * @param string $fontfile The font definition file. By default, the name is built from the family and style, in lower case with no spaces. 3058 * @access public 3059 * @since 1.0 3060 * @see AddFont(), SetFontSize() 3061 */ 3062 public function SetFont($family, $style='', $size=0, $fontfile='') { 3063 //Select a font; size given in points 3064 if ($size == 0) { 3065 $size = $this->FontSizePt; 3066 } 3067 // try to add font (if not already added) 3068 $fontdata = $this->AddFont($family, $style, $fontfile); 3069 $this->FontFamily = $fontdata['family']; 3070 $this->FontStyle = $fontdata['style']; 3071 $this->CurrentFont = $this->getFontBuffer($fontdata['fontkey']); 3072 $this->SetFontSize($size); 3073 } 3074 3075 /** 3076 * Defines the size of the current font. 3077 * @param float $size The size (in points) 3078 * @access public 3079 * @since 1.0 3080 * @see SetFont() 3081 */ 3082 public function SetFontSize($size) { 3083 //Set font size in points 3084 $this->FontSizePt = $size; 3085 $this->FontSize = $size / $this->k; 3086 if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) { 3087 $this->FontAscent = $this->CurrentFont['desc']['Ascent'] * $this->FontSize / 1000; 3088 } else { 3089 $this->FontAscent = 0.8 * $this->FontSize; 3090 } 3091 if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] > 0)) { 3092 $this->FontDescent = - $this->CurrentFont['desc']['Descent'] * $this->FontSize / 1000; 3093 } else { 3094 $this->FontDescent = 0.2 * $this->FontSize; 3095 } 3096 if (($this->page > 0) AND (isset($this->CurrentFont['i']))) { 3097 $this->_out(sprintf('BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt)); 3098 } 3099 } 3100 3101 /** 3102 * Defines the default monospaced font. 3103 * @param string $font Font name. 3104 * @access public 3105 * @since 4.5.025 3106 */ 3107 public function SetDefaultMonospacedFont($font) { 3108 $this->default_monospaced_font = $font; 3109 } 3110 3111 /** 3112 * 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 /> 3113 * The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink(). 3114 * @access public 3115 * @since 1.5 3116 * @see Cell(), Write(), Image(), Link(), SetLink() 3117 */ 3118 public function AddLink() { 3119 //Create a new internal link 3120 $n = count($this->links) + 1; 3121 $this->links[$n] = array(0, 0); 3122 return $n; 3123 } 3124 3125 /** 3126 * Defines the page and position a link points to. 3127 * @param int $link The link identifier returned by AddLink() 3128 * @param float $y Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page) 3129 * @param int $page Number of target page; -1 indicates the current page. This is the default value 3130 * @access public 3131 * @since 1.5 3132 * @see AddLink() 3133 */ 3134 public function SetLink($link, $y=0, $page=-1) { 3135 if ($y == -1) { 3136 $y = $this->y; 3137 } 3138 if ($page == -1) { 3139 $page = $this->page; 3140 } 3141 $this->links[$link] = array($page, $y); 3142 } 3143 3144 /** 3145 * Puts a link on a rectangular area of the page. 3146 * 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. 3147 * @param float $x Abscissa of the upper-left corner of the rectangle 3148 * @param float $y Ordinate of the upper-left corner of the rectangle 3149 * @param float $w Width of the rectangle 3150 * @param float $h Height of the rectangle 3151 * @param mixed $link URL or identifier returned by AddLink() 3152 * @param int $spaces number of spaces on the text to link 3153 * @access public 3154 * @since 1.5 3155 * @see AddLink(), Annotation(), Cell(), Write(), Image() 3156 */ 3157 public function Link($x, $y, $w, $h, $link, $spaces=0) { 3158 $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'), $spaces); 3159 } 3160 3161 /** 3162 * Puts a markup annotation on a rectangular area of the page. 3163 * !!!!THE ANNOTATION SUPPORT IS NOT YET FULLY IMPLEMENTED !!!! 3164 * @param float $x Abscissa of the upper-left corner of the rectangle 3165 * @param float $y Ordinate of the upper-left corner of the rectangle 3166 * @param float $w Width of the rectangle 3167 * @param float $h Height of the rectangle 3168 * @param string $text annotation text or alternate content 3169 * @param array $opt array of options (see section 8.4 of PDF reference 1.7). 3170 * @param int $spaces number of spaces on the text to link 3171 * @access public 3172 * @since 4.0.018 (2008-08-06) 3173 */ 3174 public function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text'), $spaces=0) { 3175 // recalculate coordinates to account for graphic transformations 3176 if (isset($this->transfmatrix)) { 3177 $maxid = count($this->transfmatrix) - 1; 3178 for ($i=$maxid; $i >= 0; $i--) { 3179 $ctm = $this->transfmatrix[$i]; 3180 if (isset($ctm['a'])) { 3181 $x = $x * $this->k; 3182 $y = ($this->h - $y) * $this->k; 3183 $w = $w * $this->k; 3184 $h = $h * $this->k; 3185 // top left 3186 $xt = $x; 3187 $yt = $y; 3188 $x1 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e']; 3189 $y1 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f']; 3190 // top right 3191 $xt = $x + $w; 3192 $yt = $y; 3193 $x2 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e']; 3194 $y2 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f']; 3195 // bottom left 3196 $xt = $x; 3197 $yt = $y - $h; 3198 $x3 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e']; 3199 $y3 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f']; 3200 // bottom right 3201 $xt = $x + $w; 3202 $yt = $y - $h; 3203 $x4 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e']; 3204 $y4 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f']; 3205 // new coordinates (rectangle area) 3206 $x = min($x1, $x2, $x3, $x4); 3207 $y = max($y1, $y2, $y3, $y4); 3208 $w = (max($x1, $x2, $x3, $x4) - $x) / $this->k; 3209 $h = ($y - min($y1, $y2, $y3, $y4)) / $this->k; 3210 $x = $x / $this->k; 3211 $y = $this->h - ($y / $this->k); 3212 } 3213 } 3214 } 3215 $this->PageAnnots[$this->page][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces); 3216 if (($opt['Subtype'] == 'FileAttachment') AND (!$this->empty_string($opt['FS'])) AND file_exists($opt['FS']) AND (!isset($this->embeddedfiles[basename($opt['FS'])]))) { 3217 $this->embeddedfiles[basename($opt['FS'])] = array('file' => $opt['FS'], 'n' => (count($this->embeddedfiles) + 100000)); 3218 } 3219 } 3220 3221 /** 3222 * Embedd the attached files. 3223 * @since 4.4.000 (2008-12-07) 3224 * @access protected 3225 * @see Annotation() 3226 */ 3227 protected function _putEmbeddedFiles() { 3228 reset($this->embeddedfiles); 3229 foreach ($this->embeddedfiles as $filename => $filedata) { 3230 $data = file_get_contents($filedata['file']); 3231 $filter = ''; 3232 if ($this->compress) { 3233 $data = gzcompress($data); 3234 $filter = ' /Filter /FlateDecode'; 3235 } 3236 $this->offsets[$filedata['n']] = $this->bufferlen; 3237 $this->_out($filedata['n'].' 0 obj'); 3238 $this->_out('<</Type /EmbeddedFile'.$filter.' /Length '.strlen($data).' >>'); 3239 $this->_putstream($data); 3240 $this->_out('endobj'); 3241 } 3242 } 3243 3244 /** 3245 * Prints a character string. 3246 * The origin is on the left of the first charcter, on the baseline. 3247 * This method allows to place a string precisely on the page. 3248 * @param float $x Abscissa of the origin 3249 * @param float $y Ordinate of the origin 3250 * @param string $txt String to print 3251 * @param int $stroke outline size in points (0 = disable) 3252 * @param boolean $clip if true activate clipping mode (you must call StartTransform() before this function and StopTransform() to stop the clipping tranformation). 3253 * @access public 3254 * @since 1.0 3255 * @deprecated deprecated since version 4.3.005 (2008-11-25) 3256 * @see Cell(), Write(), MultiCell(), WriteHTML(), WriteHTMLCell() 3257 */ 3258 public function Text($x, $y, $txt, $stroke=0, $clip=false) { 3259 //Output a string 3260 if ($this->rtl) { 3261 // bidirectional algorithm (some chars may be changed affecting the line length) 3262 $s = $this->utf8Bidi($this->UTF8StringToArray($txt), $txt, $this->tmprtl); 3263 $l = $this->GetArrStringWidth($s); 3264 $xr = $this->w - $x - $this->GetArrStringWidth($s); 3265 } else { 3266 $xr = $x; 3267 } 3268 $opt = ''; 3269 if (($stroke > 0) AND (!$clip)) { 3270 $opt .= '1 Tr '.intval($stroke).' w '; 3271 } elseif (($stroke > 0) AND $clip) { 3272 $opt .= '5 Tr '.intval($stroke).' w '; 3273 } elseif ($clip) { 3274 $opt .= '7 Tr '; 3275 } 3276 $s = sprintf('BT %.2F %.2F Td %s(%s) Tj ET 0 Tr', $xr * $this->k, ($this->h-$y) * $this->k, $opt, $this->_escapetext($txt)); 3277 if ($this->underline AND ($txt!='')) { 3278 $s .= ' '.$this->_dounderline($xr, $y, $txt); 3279 } 3280 if ($this->linethrough AND ($txt!='')) { 3281 $s .= ' '.$this->_dolinethrough($xr, $y, $txt); 3282 } 3283 if ($this->ColorFlag AND (!$clip)) { 3284 $s='q '.$this->TextColor.' '.$s.' Q'; 3285 } 3286 $this->_out($s); 3287 } 3288 3289 /** 3290 * Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value. 3291 * The default implementation returns a value according to the mode selected by SetAutoPageBreak().<br /> 3292 * This method is called automatically and should not be called directly by the application. 3293 * @return boolean 3294 * @access public 3295 * @since 1.4 3296 * @see SetAutoPageBreak() 3297 */ 3298 public function AcceptPageBreak() { 3299 return $this->AutoPageBreak; 3300 } 3301 3302 /** 3303 * Add page if needed. 3304 * @param float $h Cell height. Default value: 0. 3305 * @param mixed $y starting y position, leave empty for current position. 3306 * @return boolean true in case of page break, false otherwise. 3307 * @since 3.2.000 (2008-07-01) 3308 * @access protected 3309 */ 3310 protected function checkPageBreak($h=0, $y='') { 3311 if ($this->empty_string($y)) { 3312 $y = $this->y; 3313 } 3314 if ((($y + $h) > $this->PageBreakTrigger) AND (!$this->InFooter) AND ($this->AcceptPageBreak())) { 3315 //Automatic page break 3316 $x = $this->x; 3317 $this->AddPage($this->CurOrientation); 3318 $this->y = $this->tMargin; 3319 $oldpage = $this->page - 1; 3320 if ($this->rtl) { 3321 if ($this->pagedim[$this->page]['orm'] != $this->pagedim[$oldpage]['orm']) { 3322 $this->x = $x - ($this->pagedim[$this->page]['orm'] - $this->pagedim[$oldpage]['orm']); 3323 } else { 3324 $this->x = $x; 3325 } 3326 } else { 3327 if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) { 3328 $this->x = $x + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$oldpage]['olm']); 3329 } else { 3330 $this->x = $x; 3331 } 3332 } 3333 return true; 3334 } 3335 return false; 3336 } 3337 3338 /** 3339 * 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 /> 3340 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting. 3341 * @param float $w Cell width. If 0, the cell extends up to the right margin. 3342 * @param float $h Cell height. Default value: 0. 3343 * @param string $txt String to print. Default value: empty string. 3344 * @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> 3345 * @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> 3346 Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0. 3347 * @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> 3348 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. 3349 * @param mixed $link URL or identifier returned by AddLink(). 3350 * @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> 3351 * @param boolean $ignore_min_height if true ignore automatic minimum height value. 3352 * @access public 3353 * @since 1.0 3354 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak() 3355 */ 3356 public function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0, $ignore_min_height=false) { 3357 //$min_cell_height = $this->FontAscent + $this->FontDescent; 3358 $min_cell_height = $this->FontSize * $this->cell_height_ratio; 3359 if ($h < $min_cell_height) { 3360 $h = $min_cell_height; 3361 } 3362 $this->checkPageBreak($h); 3363 $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height)); 3364 } 3365 3366 /** 3367 * Removes SHY characters from text. 3368 * @param string $txt input string 3369 * @return string without SHY characters. 3370 * @access public 3371 * @since (4.5.019) 2009-02-28 3372 */ 3373 public function removeSHY($txt='') { 3374 /* 3375 * Unicode Data 3376 * Name : SOFT HYPHEN, commonly abbreviated as SHY 3377 * HTML Entity (decimal): ­ 3378 * HTML Entity (hex): ­ 3379 * HTML Entity (named): ­ 3380 * How to type in Microsoft Windows: [Alt +00AD] or [Alt 0173] 3381 * UTF-8 (hex): 0xC2 0xAD (c2ad) 3382 * UTF-8 character: chr(194).chr(173) 3383 */ 3384 $txt = preg_replace('/([\\xc2]{1}[\\xad]{1})/', '', $txt); 3385 if (!$this->isunicode) { 3386 $txt = preg_replace('/([\\xad]{1})/', '', $txt); 3387 } 3388 return $txt; 3389 } 3390 3391 /** 3392 * 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 /> 3393 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting. 3394 * @param float $w Cell width. If 0, the cell extends up to the right margin. 3395 * @param float $h Cell height. Default value: 0. 3396 * @param string $txt String to print. Default value: empty string. 3397 * @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> 3398 * @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. 3399 * @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> 3400 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. 3401 * @param mixed $link URL or identifier returned by AddLink(). 3402 * @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> 3403 * @param boolean $ignore_min_height if true ignore automatic minimum height value. 3404 * @access protected 3405 * @since 1.0 3406 * @see Cell() 3407 */ 3408 protected function getCellCode($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0, $ignore_min_height=false) { 3409 $txt = $this->removeSHY($txt); 3410 $rs = ''; //string to be returned 3411 if (!$ignore_min_height) { 3412 $min_cell_height = $this->FontSize * $this->cell_height_ratio; 3413 if ($h < $min_cell_height) { 3414 $h = $min_cell_height; 3415 } 3416 } 3417 $k = $this->k; 3418 if ($this->empty_string($w) OR ($w <= 0)) { 3419 if ($this->rtl) { 3420 $w = $this->x - $this->lMargin; 3421 } else { 3422 $w = $this->w - $this->rMargin - $this->x; 3423 } 3424 } 3425 $s = ''; 3426 if (($fill == 1) OR ($border == 1)) { 3427 if ($fill == 1) { 3428 $op = ($border == 1) ? 'B' : 'f'; 3429 } else { 3430 $op = 'S'; 3431 } 3432 if ($this->rtl) { 3433 $xk = (($this->x - $w) * $k); 3434 } else { 3435 $xk = ($this->x * $k); 3436 } 3437 $s .= sprintf('%.2F %.2F %.2F %.2F re %s ', $xk, (($this->h - $this->y) * $k), ($w * $k), (-$h * $k), $op); 3438 } 3439 if (is_string($border)) { 3440 $lm = ($this->LineWidth / 2); 3441 $x = $this->x; 3442 $y = $this->y; 3443 if (strpos($border,'L') !== false) { 3444 if ($this->rtl) { 3445 $xk = ($x - $w) * $k; 3446 } else { 3447 $xk = $x * $k; 3448 } 3449 $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - $y + $lm) * $k), $xk, (($this->h - ($y + $h + $lm)) * $k)); 3450 } 3451 if (strpos($border,'T') !== false) { 3452 if ($this->rtl) { 3453 $xk = ($x - $w + $lm) * $k; 3454 $xwk = ($x - $lm) * $k; 3455 } else { 3456 $xk = ($x - $lm) * $k; 3457 $xwk = ($x + $w + $lm) * $k; 3458 } 3459 $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - $y) * $k), $xwk, (($this->h - $y) * $k)); 3460 } 3461 if (strpos($border,'R') !== false) { 3462 if ($this->rtl) { 3463 $xk = $x * $k; 3464 } else { 3465 $xk = ($x + $w) * $k; 3466 } 3467 $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - $y + $lm) * $k), $xk, (($this->h - ($y + $h + $lm))* $k)); 3468 } 3469 if (strpos($border,'B') !== false) { 3470 if ($this->rtl) { 3471 $xk = ($x - $w + $lm) * $k; 3472 $xwk = ($x - $lm) * $k; 3473 } else { 3474 $xk = ($x - $lm) * $k; 3475 $xwk = ($x + $w + $lm) * $k; 3476 } 3477 $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - ($y + $h)) * $k), $xwk, (($this->h - ($y + $h)) * $k)); 3478 } 3479 } 3480 if ($txt != '') { 3481 // text lenght 3482 $width = $this->GetStringWidth($txt); 3483 // ratio between cell lenght and text lenght 3484 $ratio = ($w - (2 * $this->cMargin)) / $width; 3485 3486 // stretch text if required 3487 if (($stretch > 0) AND (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0)))) { 3488 if ($stretch > 2) { 3489 // spacing 3490 //Calculate character spacing in points 3491 $char_space = (($w - $width - (2 * $this->cMargin)) * $this->k) / max($this->GetNumChars($txt)-1,1); 3492 //Set character spacing 3493 $rs .= sprintf('BT %.2F Tc ET ', $char_space); 3494 } else { 3495 // scaling 3496 //Calculate horizontal scaling 3497 $horiz_scale = $ratio * 100.0; 3498 //Set horizontal scaling 3499 $rs .= sprintf('BT %.2F Tz ET ', $horiz_scale); 3500 } 3501 $align = ''; 3502 $width = $w - (2 * $this->cMargin); 3503 } else { 3504 $stretch == 0; 3505 } 3506 if ($align == 'L') { 3507 if ($this->rtl) { 3508 $dx = $w - $width - $this->cMargin; 3509 } else { 3510 $dx = $this->cMargin; 3511 } 3512 } elseif ($align == 'R') { 3513 if ($this->rtl) { 3514 $dx = $this->cMargin; 3515 } else { 3516 $dx = $w - $width - $this->cMargin; 3517 } 3518 } elseif ($align == 'C') { 3519 $dx = ($w - $width) / 2; 3520 } elseif ($align == 'J') { 3521 if ($this->rtl) { 3522 $dx = $w - $width - $this->cMargin; 3523 } else { 3524 $dx = $this->cMargin; 3525 } 3526 } else { 3527 $dx = $this->cMargin; 3528 } 3529 if ($this->ColorFlag) { 3530 $s .= 'q '.$this->TextColor.' '; 3531 } 3532 $txt2 = $this->_escapetext($txt); 3533 if ($this->rtl) { 3534 $xdk = ($this->x - $dx - $width) * $k; 3535 } else { 3536 $xdk = ($this->x + $dx) * $k; 3537 } 3538 // Justification 3539 if ($align == 'J') { 3540 // count number of spaces 3541 $ns = substr_count($txt, ' '); 3542 if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) { 3543 // get string width without spaces 3544 $width = $this->GetStringWidth(str_replace(' ', '', $txt)); 3545 // calculate average space width 3546 $spacewidth = ($w - $width - (2 * $this->cMargin)) / ($ns?$ns:1) / $this->FontSize / $this->k; 3547 // set word position to be used with TJ operator 3548 $txt2 = str_replace(chr(0).' ', ') '.(-2830 * $spacewidth).' (', $txt2); 3549 } else { 3550 // get string width 3551 $width = $this->GetStringWidth($txt); 3552 $spacewidth = (($w - $width - (2 * $this->cMargin)) / ($ns?$ns:1)) * $this->k; 3553 $rs .= sprintf('BT %.3F Tw ET ', $spacewidth); 3554 } 3555 } 3556 // calculate approximate position of the font base line 3557 //$basefonty = $this->y + (($h + $this->FontAscent - $this->FontDescent)/2); 3558 $basefonty = $this->y + ($h/2) + ($this->FontSize/3); 3559 // print text 3560 $s .= sprintf('BT %.2F %.2F Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2); 3561 if ($this->rtl) { 3562 $xdx = $this->x - $dx - $width; 3563 } else { 3564 $xdx = $this->x + $dx; 3565 } 3566 if ($this->underline) { 3567 $s .= ' '.$this->_dounderline($xdx, $basefonty, $txt); 3568 } 3569 if ($this->linethrough) { 3570 $s .= ' '.$this->_dolinethrough($xdx, $basefonty, $txt); 3571 } 3572 if ($this->ColorFlag) { 3573 $s .= ' Q'; 3574 } 3575 if ($link) { 3576 $this->Link($xdx, $this->y + (($h - $this->FontSize)/2), $width, $this->FontSize, $link, substr_count($txt, chr(32))); 3577 } 3578 } 3579 // output cell 3580 if ($s) { 3581 // output cell 3582 $rs .= $s; 3583 // reset text stretching 3584 if ($stretch > 2) { 3585 //Reset character horizontal spacing 3586 $rs .= ' BT 0 Tc ET'; 3587 } elseif ($stretch > 0) { 3588 //Reset character horizontal scaling 3589 $rs .= ' BT 100 Tz ET'; 3590 } 3591 } 3592 // reset word spacing 3593 if (!(($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) AND ($align == 'J')) { 3594 $rs .= ' BT 0 Tw ET'; 3595 } 3596 $this->lasth = $h; 3597 if ($ln > 0) { 3598 //Go to the beginning of the next line 3599 $this->y += $h; 3600 if ($ln == 1) { 3601 if ($this->rtl) { 3602 $this->x = $this->w - $this->rMargin; 3603 } else { 3604 $this->x = $this->lMargin; 3605 } 3606 } 3607 } else { 3608 // go left or right by case 3609 if ($this->rtl) { 3610 $this->x -= $w; 3611 } else { 3612 $this->x += $w; 3613 } 3614 } 3615 $gstyles = ''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor."\n"; 3616 $rs = $gstyles.$rs; 3617 return $rs; 3618 } 3619 3620 /** 3621 * This method allows printing text with line breaks. 3622 * 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 /> 3623 * Text can be aligned, centered or justified. The cell block can be framed and the background painted. 3624 * @param float $w Width of cells. If 0, they extend up to the right margin of the page. 3625 * @param float $h Cell minimum height. The cell extends automatically if needed. 3626 * @param string $txt String to print 3627 * @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> 3628 * @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> 3629 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. 3630 * @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> 3631 * @param int $x x position in user units 3632 * @param int $y y position in user units 3633 * @param boolean $reseth if true reset the last cell height (default true). 3634 * @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> 3635 * @param boolean $ishtml set to true if $txt is HTML content (default = false). 3636 * @param boolean $autopadding if true, uses internal padding and automatically adjust it to account for line width. 3637 * @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. 3638 * @return int Return the number of cells or 1 for html mode. 3639 * @access public 3640 * @since 1.3 3641 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak() 3642 */ 3643 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) { 3644 if ($this->empty_string($this->lasth) OR $reseth) { 3645 //set row height 3646 $this->lasth = $this->FontSize * $this->cell_height_ratio; 3647 } 3648 if (!$this->empty_string($y)) { 3649 $this->SetY($y); 3650 } else { 3651 $y = $this->GetY(); 3652 } 3653 // check for page break 3654 $this->checkPageBreak($h); 3655 $y = $this->GetY(); 3656 // get current page number 3657 $startpage = $this->page; 3658 if (!$this->empty_string($x)) { 3659 $this->SetX($x); 3660 } else { 3661 $x = $this->GetX(); 3662 } 3663 if ($this->empty_string($w) OR ($w <= 0)) { 3664 if ($this->rtl) { 3665 $w = $this->x - $this->lMargin; 3666 } else { 3667 $w = $this->w - $this->rMargin - $this->x; 3668 } 3669 } 3670 // store original margin values 3671 $lMargin = $this->lMargin; 3672 $rMargin = $this->rMargin; 3673 if ($this->rtl) { 3674 $this->SetRightMargin($this->w - $this->x); 3675 $this->SetLeftMargin($this->x - $w); 3676 } else { 3677 $this->SetLeftMargin($this->x); 3678 $this->SetRightMargin($this->w - $this->x - $w); 3679 } 3680 $starty = $this->y; 3681 if ($autopadding) { 3682 // Adjust internal padding 3683 if ($this->cMargin < ($this->LineWidth / 2)) { 3684 $this->cMargin = ($this->LineWidth / 2); 3685 } 3686 // Add top space if needed 3687 if (($this->lasth - $this->FontSize) < $this->LineWidth) { 3688 $this->y += $this->LineWidth / 2; 3689 } 3690 // add top padding 3691 $this->y += $this->cMargin; 3692 } 3693 if ($ishtml) { 3694 // ******* Write HTML text 3695 $this->writeHTML($txt, true, 0, $reseth, true, $align); 3696 $nl = 1; 3697 } else { 3698 // ******* Write text 3699 $nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false, false, $maxh); 3700 } 3701 if ($autopadding) { 3702 // add bottom padding 3703 $this->y += $this->cMargin; 3704 // Add bottom space if needed 3705 if (($this->lasth - $this->FontSize) < $this->LineWidth) { 3706 $this->y += $this->LineWidth / 2; 3707 } 3708 } 3709 // Get end-of-text Y position 3710 $currentY = $this->y; 3711 // get latest page number 3712 $endpage = $this->page; 3713 // check if a new page has been created 3714 if ($endpage > $startpage) { 3715 // design borders around HTML cells. 3716 for ($page=$startpage; $page <= $endpage; ++$page) { 3717 $this->setPage($page); 3718 if ($page == $startpage) { 3719 $this->y = $starty; // put cursor at the beginning of cell on the first page 3720 $h = $this->getPageHeight() - $starty - $this->getBreakMargin(); 3721 $cborder = $this->getBorderMode($border, $position='start'); 3722 } elseif ($page == $endpage) { 3723 $this->y = $this->tMargin; // put cursor at the beginning of last page 3724 $h = $currentY - $this->tMargin; 3725 $cborder = $this->getBorderMode($border, $position='end'); 3726 } else { 3727 $this->y = $this->tMargin; // put cursor at the beginning of the current page 3728 $h = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin(); 3729 $cborder = $this->getBorderMode($border, $position='middle'); 3730 } 3731 $nx = $x; 3732 // account for margin changes 3733 if ($page > $startpage) { 3734 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) { 3735 $nx = $x + ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']); 3736 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) { 3737 $nx = $x + ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']); 3738 } 3739 } 3740 $this->SetX($nx); 3741 $ccode = $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, false); 3742 if ($cborder OR $fill) { 3743 $pagebuff = $this->getPageBuffer($this->page); 3744 $pstart = substr($pagebuff, 0, $this->intmrk[$this->page]); 3745 $pend = substr($pagebuff, $this->intmrk[$this->page]); 3746 $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend); 3747 $this->intmrk[$this->page] += strlen($ccode."\n"); 3748 } 3749 } 3750 } else { 3751 $h = max($h, ($currentY - $y)); 3752 // put cursor at the beginning of text 3753 $this->SetY($y); 3754 $this->SetX($x); 3755 // design a cell around the text 3756 $ccode = $this->getCellCode($w, $h, '', $border, 1, '', $fill, '', 0, true); 3757 if ($border OR $fill) { 3758 if (end($this->transfmrk[$this->page]) !== false) { 3759 $pagemarkkey = key($this->transfmrk[$this->page]); 3760 $pagemark = &$this->transfmrk[$this->page][$pagemarkkey]; 3761 } elseif ($this->InFooter) { 3762 $pagemark = &$this->footerpos[$this->page]; 3763 } else { 3764 $pagemark = &$this->intmrk[$this->page]; 3765 } 3766 $pagebuff = $this->getPageBuffer($this->page); 3767 $pstart = substr($pagebuff, 0, $pagemark); 3768 $pend = substr($pagebuff, $pagemark); 3769 $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend); 3770 $pagemark += strlen($ccode."\n"); 3771 } 3772 } 3773 // Get end-of-cell Y position 3774 $currentY = $this->GetY(); 3775 // restore original margin values 3776 $this->SetLeftMargin($lMargin); 3777 $this->SetRightMargin($rMargin); 3778 if ($ln > 0) { 3779 //Go to the beginning of the next line 3780 $this->SetY($currentY); 3781 if ($ln == 2) { 3782 $this->SetX($x + $w); 3783 } 3784 } else { 3785 // go left or right by case 3786 $this->setPage($startpage); 3787 $this->y = $y; 3788 $this->SetX($x + $w); 3789 } 3790 return $nl; 3791 } 3792 3793 /** 3794 * Get the border mode accounting for multicell position (opens bottom side of multicell crossing pages) 3795 * @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> 3796 * @param string multicell position: 'start', 'middle', 'end' 3797 * @return border mode 3798 * @access protected 3799 * @since 4.4.002 (2008-12-09) 3800 */ 3801 protected function getBorderMode($border, $position='start') { 3802 if ((!$this->opencell) AND ($border == 1)) { 3803 return 1; 3804 } 3805 $cborder = ''; 3806 switch ($position) { 3807 case 'start': { 3808 if ($border == 1) { 3809 $cborder = 'LTR'; 3810 } else { 3811 if (!(false === strpos($border, 'L'))) { 3812 $cborder .= 'L'; 3813 } 3814 if (!(false === strpos($border, 'T'))) { 3815 $cborder .= 'T'; 3816 } 3817 if (!(false === strpos($border, 'R'))) { 3818 $cborder .= 'R'; 3819 } 3820 if ((!$this->opencell) AND (!(false === strpos($border, 'B')))) { 3821 $cborder .= 'B'; 3822 } 3823 } 3824 break; 3825 } 3826 case 'middle': { 3827 if ($border == 1) { 3828 $cborder = 'LR'; 3829 } else { 3830 if (!(false === strpos($border, 'L'))) { 3831 $cborder .= 'L'; 3832 } 3833 if ((!$this->opencell) AND (!(false === strpos($border, 'T')))) { 3834 $cborder .= 'T'; 3835 } 3836 if (!(false === strpos($border, 'R'))) { 3837 $cborder .= 'R'; 3838 } 3839 if ((!$this->opencell) AND (!(false === strpos($border, 'B')))) { 3840 $cborder .= 'B'; 3841 } 3842 } 3843 break; 3844 } 3845 case 'end': { 3846 if ($border == 1) { 3847 $cborder = 'LRB'; 3848 } else { 3849 if (!(false === strpos($border, 'L'))) { 3850 $cborder .= 'L'; 3851 } 3852 if ((!$this->opencell) AND (!(false === strpos($border, 'T')))) { 3853 $cborder .= 'T'; 3854 } 3855 if (!(false === strpos($border, 'R'))) { 3856 $cborder .= 'R'; 3857 } 3858 if (!(false === strpos($border, 'B'))) { 3859 $cborder .= 'B'; 3860 } 3861 } 3862 break; 3863 } 3864 default: { 3865 $cborder = $border; 3866 break; 3867 } 3868 } 3869 return $cborder; 3870 } 3871 3872 /** 3873 * This method returns the estimated number of lines required to print the text. 3874 * @param string $txt text to print 3875 * @param float $w width of cell. If 0, they extend up to the right margin of the page. 3876 * @return int Return the estimated number of lines. 3877 * @access public 3878 * @since 4.5.011 3879 */ 3880 public function getNumLines($txt, $w=0) { 3881 $lines = 0; 3882 if ($this->empty_string($w) OR ($w <= 0)) { 3883 if ($this->rtl) { 3884 $w = $this->x - $this->lMargin; 3885 } else { 3886 $w = $this->w - $this->rMargin - $this->x; 3887 } 3888 } 3889 // max column width 3890 $wmax = $w - (2 * $this->cMargin); 3891 // remove carriage returns 3892 $txt = str_replace("\r", '', $txt); 3893 // remove last newline (if any) 3894 if (substr($txt,-1) == "\n") { 3895 $txt = substr($txt, 0, -1); 3896 } 3897 // divide text in blocks 3898 $txtblocks = explode("\n", $txt); 3899 // for each block; 3900 foreach ($txtblocks as $block) { 3901 // estimate the number of lines 3902 $lines += $this->empty_string($block) ? 1 : (ceil($this->GetStringWidth($block) / $wmax)); 3903 } 3904 return $lines; 3905 } 3906 3907 /** 3908 * This method prints text from the current position.<br /> 3909 * @param float $h Line height 3910 * @param string $txt String to print 3911 * @param mixed $link URL or identifier returned by AddLink() 3912 * @param int $fill Indicates if the background must be painted (1) or transparent (0). Default value: 0. 3913 * @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> 3914 * @param boolean $ln if true set cursor at the bottom of the line, otherwise set cursor at the top of the line. 3915 * @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> 3916 * @param boolean $firstline if true prints only the first line and return the remaining string. 3917 * @param boolean $firstblock if true the string is the starting of a line. 3918 * @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. 3919 * @return mixed Return the number of cells or the remaining string if $firstline = true. 3920 * @access public 3921 * @since 1.5 3922 */ 3923 public function Write($h, $txt, $link='', $fill=0, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0) { 3924 if (strlen($txt) == 0) { 3925 $txt = ' '; 3926 } 3927 // remove carriage returns 3928 $s = str_replace("\r", '', $txt); 3929 // check if string contains arabic text 3930 if (preg_match(K_RE_PATTERN_ARABIC, $s)) { 3931 $arabic = true; 3932 } else { 3933 $arabic = false; 3934 } 3935 // check if string contains RTL text 3936 if ($arabic OR $this->tmprtl OR preg_match(K_RE_PATTERN_RTL, $txt)) { 3937 $rtlmode = true; 3938 } else { 3939 $rtlmode = false; 3940 } 3941 // get a char width 3942 $chrwidth = $this->GetCharWidth('.'); 3943 // get array of unicode values 3944 $chars = $this->UTF8StringToArray($s); 3945 // get array of chars 3946 $uchars = $this->UTF8ArrayToUniArray($chars); 3947 // get the number of characters 3948 $nb = count($chars); 3949 // replacement for SHY character (minus symbol) 3950 $shy_replacement = 45; 3951 $shy_replacement_char = $this->unichr($shy_replacement); 3952 // widht for SHY replacement 3953 $shy_replacement_width = $this->GetCharWidth($shy_replacement); 3954 // store current position 3955 $prevx = $this->x; 3956 $prevy = $this->y; 3957 // max Y 3958 $maxy = $this->y + $maxh - $h - (2 * $this->cMargin); 3959 // calculate remaining line width ($w) 3960 if ($this->rtl) { 3961 $w = $this->x - $this->lMargin; 3962 } else { 3963 $w = $this->w - $this->rMargin - $this->x; 3964 } 3965 // max column width 3966 $wmax = $w - (2 * $this->cMargin); 3967 $i = 0; // character position 3968 $j = 0; // current starting position 3969 $sep = -1; // position of the last blank space 3970 $shy = false; // true if the last blank is a soft hypen (SHY) 3971 $l = 0; // current string lenght 3972 $nl = 0; //number of lines 3973 $linebreak = false; 3974 // for each character 3975 while ($i < $nb) { 3976 if (($maxh > 0) AND ($this->y >= $maxy) ) { 3977 $firstline = true; 3978 } 3979 //Get the current character 3980 $c = $chars[$i]; 3981 if ($c == 10) { // 10 = "\n" = new line 3982 //Explicit line break 3983 if ($align == 'J') { 3984 if ($this->rtl) { 3985 $talign = 'R'; 3986 } else { 3987 $talign = 'L'; 3988 } 3989 } else { 3990 $talign = $align; 3991 } 3992 $tmpstr = $this->UniArrSubString($uchars, $j, $i); 3993 if ($firstline) { 3994 $startx = $this->x; 3995 $tmparr = array_slice($chars, $j, $i); 3996 if ($rtlmode) { 3997 $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl); 3998 } 3999 $linew = $this->GetArrStringWidth($tmparr); 4000 unset($tmparr); 4001 if ($this->rtl) { 4002 $this->endlinex = $startx - $linew; 4003 } else { 4004 $this->endlinex = $startx + $linew; 4005 } 4006 $w = $linew; 4007 $tmpcmargin = $this->cMargin; 4008 if ($maxh == 0) { 4009 $this->cMargin = 0; 4010 } 4011 } 4012 $this->Cell($w, $h, $tmpstr, 0, 1, $talign, $fill, $link, $stretch); 4013 unset($tmpstr); 4014 if ($firstline) { 4015 $this->cMargin = $tmpcmargin; 4016 return ($this->UniArrSubString($uchars, $i)); 4017 } 4018 ++$nl; 4019 $j = $i + 1; 4020 $l = 0; 4021 $sep = -1; 4022 $shy = false; 4023 // account for margin changes 4024 if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND (!$this->InFooter)) { 4025 // AcceptPageBreak() may be overriden on extended classed to include margin changes 4026 $this->AcceptPageBreak(); 4027 } 4028 $w = $this->getRemainingWidth(); 4029 $wmax = $w - (2 * $this->cMargin); 4030 } else { 4031 // 160 is the non-breaking space. 4032 // 173 is SHY (Soft Hypen). 4033 // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator. 4034 // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants. 4035 // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between. 4036 if (($c != 160) AND (($c == 173) OR preg_match($this->re_spaces, $this->unichr($c)))) { 4037 // update last blank space position 4038 $sep = $i; 4039 // check if is a SHY 4040 if ($c == 173) { 4041 $shy = true; 4042 } else { 4043 $shy = false; 4044 } 4045 } 4046 // update string length 4047 if ((($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) AND ($arabic)) { 4048 // with bidirectional algorithm some chars may be changed affecting the line length 4049 // *** very slow *** 4050 $l = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $i-$j+1), '', $this->tmprtl)); 4051 } else { 4052 $l += $this->GetCharWidth($c); 4053 } 4054 if (($l > $wmax) OR ($shy AND (($l + $shy_replacement_width) > $wmax)) ) { 4055 // we have reached the end of column 4056 if ($sep == -1) { 4057 // check if the line was already started 4058 if (($this->rtl AND ($this->x <= ($this->w - $this->rMargin - $chrwidth))) 4059 OR ((!$this->rtl) AND ($this->x >= ($this->lMargin + $chrwidth)))) { 4060 // print a void cell and go to next line 4061 $this->Cell($w, $h, '', 0, 1); 4062 $linebreak = true; 4063 if ($firstline) { 4064 return ($this->UniArrSubString($uchars, $j)); 4065 } 4066 } else { 4067 // truncate the word because do not fit on column 4068 $tmpstr = $this->UniArrSubString($uchars, $j, $i); 4069 if ($firstline) { 4070 $startx = $this->x; 4071 $tmparr = array_slice($chars, $j, $i); 4072 if ($rtlmode) { 4073 $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl); 4074 } 4075 $linew = $this->GetArrStringWidth($tmparr); 4076 unset($tmparr); 4077 if ($this->rtl) { 4078 $this->endlinex = $startx - $linew; 4079 } else { 4080 $this->endlinex = $startx + $linew; 4081 } 4082 $w = $linew; 4083 $tmpcmargin = $this->cMargin; 4084 if ($maxh == 0) { 4085 $this->cMargin = 0; 4086 } 4087 } 4088 $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch); 4089 unset($tmpstr); 4090 if ($firstline) { 4091 $this->cMargin = $tmpcmargin; 4092 return ($this->UniArrSubString($uchars, $i)); 4093 } 4094 $j = $i; 4095 --$i; 4096 } 4097 } else { 4098 // word wrapping 4099 if ($this->rtl AND (!$firstblock)) { 4100 $endspace = 1; 4101 } else { 4102 $endspace = 0; 4103 } 4104 if ($shy) { 4105 // add hypen (minus symbol) at the end of the line 4106 $shy_width = $shy_replacement_width; 4107 if ($this->rtl) { 4108 $shy_char_left = $shy_replacement_char; 4109 $shy_char_right = ''; 4110 } else { 4111 $shy_char_left = ''; 4112 $shy_char_right = $shy_replacement_char; 4113 } 4114 } else { 4115 $shy_width = 0; 4116 $shy_char_left = ''; 4117 $shy_char_right = ''; 4118 } 4119 $tmpstr = $this->UniArrSubString($uchars, $j, ($sep + $endspace)); 4120 if ($firstline) { 4121 $startx = $this->x; 4122 $tmparr = array_slice($chars, $j, ($sep + $endspace)); 4123 if ($rtlmode) { 4124 $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl); 4125 } 4126 $linew = $this->GetArrStringWidth($tmparr); 4127 unset($tmparr); 4128 if ($this->rtl) { 4129 $this->endlinex = $startx - $linew - $shy_width; 4130 } else { 4131 $this->endlinex = $startx + $linew + $shy_width; 4132 } 4133 $w = $linew; 4134 $tmpcmargin = $this->cMargin; 4135 if ($maxh == 0) { 4136 $this->cMargin = 0; 4137 } 4138 } 4139 // print the line 4140 $this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch); 4141 unset($tmpstr); 4142 if ($firstline) { 4143 // return the remaining text 4144 $this->cMargin = $tmpcmargin; 4145 return ($this->UniArrSubString($uchars, ($sep + $endspace))); 4146 } 4147 $i = $sep; 4148 $sep = -1; 4149 $shy = false; 4150 $j = ($i+1); 4151 } 4152 // account for margin changes 4153 if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND (!$this->InFooter)) { 4154 // AcceptPageBreak() may be overriden on extended classed to include margin changes 4155 $this->AcceptPageBreak(); 4156 } 4157 $w = $this->getRemainingWidth(); 4158 $wmax = $w - (2 * $this->cMargin); 4159 if ($linebreak) { 4160 $linebreak = false; 4161 } else { 4162 ++$nl; 4163 $l = 0; 4164 } 4165 } 4166 } 4167 ++$i; 4168 } // end while i < nb 4169 // print last substring (if any) 4170 if ($l > 0) { 4171 switch ($align) { 4172 case 'J': 4173 case 'C': { 4174 $w = $w; 4175 break; 4176 } 4177 case 'L': { 4178 if ($this->rtl) { 4179 $w = $w; 4180 } else { 4181 $w = $l; 4182 } 4183 break; 4184 } 4185 case 'R': { 4186 if ($this->rtl) { 4187 $w = $l; 4188 } else { 4189 $w = $w; 4190 } 4191 break; 4192 } 4193 default: { 4194 $w = $l; 4195 break; 4196 } 4197 } 4198 $tmpstr = $this->UniArrSubString($uchars, $j, $nb); 4199 if ($firstline) { 4200 $startx = $this->x; 4201 $tmparr = array_slice($chars, $j, $nb); 4202 if ($rtlmode) { 4203 $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl); 4204 } 4205 $linew = $this->GetArrStringWidth($tmparr); 4206 unset($tmparr); 4207 if ($this->rtl) { 4208 $this->endlinex = $startx - $linew; 4209 } else { 4210 $this->endlinex = $startx + $linew; 4211 } 4212 $w = $linew; 4213 $tmpcmargin = $this->cMargin; 4214 if ($maxh == 0) { 4215 $this->cMargin = 0; 4216 } 4217 } 4218 $this->Cell($w, $h, $tmpstr, 0, $ln, $align, $fill, $link, $stretch); 4219 unset($tmpstr); 4220 if ($firstline) { 4221 $this->cMargin = $tmpcmargin; 4222 return ($this->UniArrSubString($uchars, $nb)); 4223 } 4224 ++$nl; 4225 } 4226 if ($firstline) { 4227 return ''; 4228 } 4229 return $nl; 4230 } 4231 4232 /** 4233 * Returns the remaining width between the current position and margins. 4234 * @return int Return the remaining width 4235 * @access protected 4236 */ 4237 protected function getRemainingWidth() { 4238 if ($this->rtl) { 4239 return ($this->x - $this->lMargin); 4240 } else { 4241 return ($this->w - $this->rMargin - $this->x); 4242 } 4243 } 4244 4245 /** 4246 * Extract a slice of the $strarr array and return it as string. 4247 * @param string $strarr The input array of characters. 4248 * @param int $start the starting element of $strarr. 4249 * @param int $end first element that will not be returned. 4250 * @return Return part of a string 4251 * @access public 4252 */ 4253 public function UTF8ArrSubString($strarr, $start='', $end='') { 4254 if (strlen($start) == 0) { 4255 $start = 0; 4256 } 4257 if (strlen($end) == 0) { 4258 $end = count($strarr); 4259 } 4260 $string = ''; 4261 for ($i=$start; $i < $end; ++$i) { 4262 $string .= $this->unichr($strarr[$i]); 4263 } 4264 return $string; 4265 } 4266 4267 /** 4268 * Extract a slice of the $uniarr array and return it as string. 4269 * @param string $uniarr The input array of characters. 4270 * @param int $start the starting element of $strarr. 4271 * @param int $end first element that will not be returned. 4272 * @return Return part of a string 4273 * @access public 4274 * @since 4.5.037 (2009-04-07) 4275 */ 4276 public function UniArrSubString($uniarr, $start='', $end='') { 4277 if (strlen($start) == 0) { 4278 $start = 0; 4279 } 4280 if (strlen($end) == 0) { 4281 $end = count($uniarr); 4282 } 4283 $string = ''; 4284 for ($i=$start; $i < $end; ++$i) { 4285 $string .= $uniarr[$i]; 4286 } 4287 return $string; 4288 } 4289 4290 /** 4291 * Convert an array of UTF8 values to array of unicode characters 4292 * @param string $ta The input array of UTF8 values. 4293 * @return Return array of unicode characters 4294 * @access public 4295 * @since 4.5.037 (2009-04-07) 4296 */ 4297 public function UTF8ArrayToUniArray($ta) { 4298 return array_map(array($this, 'unichr'), $ta); 4299 } 4300 4301 /** 4302 * Returns the unicode caracter specified by UTF-8 code 4303 * @param int $c UTF-8 code 4304 * @return Returns the specified character. 4305 * @author Miguel Perez, Nicola Asuni 4306 * @access public 4307 * @since 2.3.000 (2008-03-05) 4308 */ 4309 public function unichr($c) { 4310 if (!$this->isunicode) { 4311 return chr($c); 4312 } elseif ($c <= 0x7F) { 4313 // one byte 4314 return chr($c); 4315 } elseif ($c <= 0x7FF) { 4316 // two bytes 4317 return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F); 4318 } elseif ($c <= 0xFFFF) { 4319 // three bytes 4320 return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F); 4321 } elseif ($c <= 0x10FFFF) { 4322 // four bytes 4323 return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F); 4324 } else { 4325 return ''; 4326 } 4327 } 4328 4329 /** 4330 * Puts an image in the page. 4331 * The upper-left corner must be given. 4332 * The dimensions can be specified in different ways:<ul> 4333 * <li>explicit width and height (expressed in user unit)</li> 4334 * <li>one explicit dimension, the other being calculated automatically in order to keep the original proportions</li> 4335 * <li>no explicit dimension, in which case the image is put at 72 dpi</li></ul> 4336 * 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; 4337 * The format can be specified explicitly or inferred from the file extension.<br /> 4338 * It is possible to put a link on the image.<br /> 4339 * Remark: if an image is used several times, only one copy will be embedded in the file.<br /> 4340 * @param string $file Name of the file containing the image. 4341 * @param float $x Abscissa of the upper-left corner. 4342 * @param float $y Ordinate of the upper-left corner. 4343 * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated. 4344 * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated. 4345 * @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. 4346 * @param mixed $link URL or identifier returned by AddLink(). 4347 * @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> 4348 * @param boolean $resize If true resize (reduce) the image to fit $w and $h (requires GD library). 4349 * @param int $dpi dot-per-inch resolution used on resize 4350 * @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> 4351 * @param boolean $ismask true if this image is a mask, false otherwise 4352 * @param mixed $imgmask image object returned by this function or false 4353 * @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> 4354 * @param boolean $fitbox If true scale image dimensions proportionally to fit within the ($w, $h) box. 4355 * @return image information 4356 * @access public 4357 * @since 1.1 4358 */ 4359 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) { 4360 if ($x === '') { 4361 $x = $this->x; 4362 } 4363 if ($y === '') { 4364 $y = $this->y; 4365 } 4366 // get image dimensions 4367 $imsize = @getimagesize($file); 4368 if ($imsize === FALSE) { 4369 // encode spaces on filename 4370 $file = str_replace(' ', '%20', $file); 4371 $imsize = @getimagesize($file); 4372 if ($imsize === FALSE) { 4373 $this->Error('[Image] No such file or directory in '.$file); 4374 } 4375 } 4376 // get original image width and height in pixels 4377 list($pixw, $pixh) = $imsize; 4378 // calculate image width and height on document 4379 if (($w <= 0) AND ($h <= 0)) { 4380 // convert image size to document unit 4381 $w = $this->pixelsToUnits($pixw); 4382 $h = $this->pixelsToUnits($pixh); 4383 } elseif ($w <= 0) { 4384 $w = $h * $pixw / $pixh; 4385 } elseif ($h <= 0) { 4386 $h = $w * $pixh / $pixw; 4387 } elseif ($fitbox AND ($w > 0) AND ($h > 0)) { 4388 // scale image dimensions proportionally to fit within the ($w, $h) box 4389 if ((($w * $pixh) / ($h * $pixw)) < 1) { 4390 $h = $w * $pixh / $pixw; 4391 } else { 4392 $w = $h * $pixw / $pixh; 4393 } 4394 } 4395 // calculate new minimum dimensions in pixels 4396 $neww = round($w * $this->k * $dpi / $this->dpi); 4397 $newh = round($h * $this->k * $dpi / $this->dpi); 4398 // check if resize is necessary (resize is used only to reduce the image) 4399 if (($neww * $newh) >= ($pixw * $pixh)) { 4400 $resize = false; 4401 } 4402 // check if image has been already added on document 4403 if (!in_array($file, $this->imagekeys)) { 4404 //First use of image, get info 4405 if ($type == '') { 4406 $fileinfo = pathinfo($file); 4407 if (isset($fileinfo['extension']) AND (!$this->empty_string($fileinfo['extension']))) { 4408 $type = $fileinfo['extension']; 4409 } else { 4410 $this->Error('Image file has no extension and no type was specified: '.$file); 4411 } 4412 } 4413 $type = strtolower($type); 4414 if ($type == 'jpg') { 4415 $type = 'jpeg'; 4416 } 4417 $mqr = get_magic_quotes_runtime(); 4418 set_magic_quotes_runtime(0); 4419 // Specific image handlers 4420 $mtd = '_parse'.$type; 4421 // GD image handler function 4422 $gdfunction = 'imagecreatefrom'.$type; 4423 $info = false; 4424 if ((method_exists($this, $mtd)) AND (!($resize AND function_exists($gdfunction)))) { 4425 // TCPDF image functions 4426 $info = $this->$mtd($file); 4427 if ($info == 'pngalpha') { 4428 return $this->ImagePngAlpha($file, $x, $y, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign); 4429 } 4430 } 4431 if (!$info) { 4432 if (function_exists($gdfunction)) { 4433 // GD library 4434 $img = $gdfunction($file); 4435 if ($resize) { 4436 $imgr = imagecreatetruecolor($neww, $newh); 4437 imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh); 4438 $info = $this->_toJPEG($imgr); 4439 } else { 4440 $info = $this->_toJPEG($img); 4441 } 4442 } elseif (extension_loaded('imagick')) { 4443 // ImageMagick library 4444 $img = new Imagick(); 4445 $img->readImage($file); 4446 if ($resize) { 4447 $img->resizeImage($neww, $newh, 10, 1, false); 4448 } 4449 $img->setCompressionQuality($this->jpeg_quality); 4450 $img->setImageFormat('jpeg'); 4451 $tempname = tempnam(K_PATH_CACHE, 'jpg_'); 4452 $img->writeImage($tempname); 4453 $info = $this->_parsejpeg($tempname); 4454 unlink($tempname); 4455 $img->destroy(); 4456 } else { 4457 return; 4458 } 4459 } 4460 if ($info === false) { 4461 //If false, we cannot process image 4462 return; 4463 } 4464 set_magic_quotes_runtime($mqr); 4465 if ($ismask) { 4466 // force grayscale 4467 $info['cs'] = 'DeviceGray'; 4468 } 4469 $info['i'] = $this->numimages + 1; 4470 if ($imgmask !== false) { 4471 $info['masked'] = $imgmask; 4472 } 4473 // add image to document 4474 $this->setImageBuffer($file, $info); 4475 } else { 4476 $info = $this->getImageBuffer($file); 4477 } 4478 // Check whether we need a new page first as this does not fit 4479 if ($this->checkPageBreak($h, $y)) { 4480 $y = $this->GetY() + $this->cMargin; 4481 } 4482 // set bottomcoordinates 4483 $this->img_rb_y = $y + $h; 4484 // set alignment 4485 if ($this->rtl) { 4486 if ($palign == 'L') { 4487 $ximg = $this->lMargin; 4488 // set right side coordinate 4489 $this->img_rb_x = $ximg + $w; 4490 } elseif ($palign == 'C') { 4491 $ximg = ($this->w - $x - $w) / 2; 4492 // set right side coordinate 4493 $this->img_rb_x = $ximg + $w; 4494 } else { 4495 $ximg = $this->w - $x - $w; 4496 // set left side coordinate 4497 $this->img_rb_x = $ximg; 4498 } 4499 } else { 4500 if ($palign == 'R') { 4501 $ximg = $this->w - $this->rMargin - $w; 4502 // set left side coordinate 4503 $this->img_rb_x = $ximg; 4504 } elseif ($palign == 'C') { 4505 $ximg = ($this->w - $x - $w) / 2; 4506 // set right side coordinate 4507 $this->img_rb_x = $ximg + $w; 4508 } else { 4509 $ximg = $x; 4510 // set right side coordinate 4511 $this->img_rb_x = $ximg + $w; 4512 } 4513 } 4514 if ($ismask) { 4515 // embed hidden, ouside the canvas 4516 $xkimg = ($this->pagedim[$this->page]['w'] + 10); 4517 } else { 4518 $xkimg = $ximg * $this->k; 4519 } 4520 $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'])); 4521 if (!empty($border)) { 4522 $bx = $x; 4523 $by = $y; 4524 $this->x = $ximg; 4525 $this->y = $y; 4526 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0); 4527 $this->x = $bx; 4528 $this->y = $by; 4529 } 4530 if ($link) { 4531 $this->Link($ximg, $y, $w, $h, $link, 0); 4532 } 4533 // set pointer to align the successive text/objects 4534 switch($align) { 4535 case 'T': { 4536 $this->y = $y; 4537 $this->x = $this->img_rb_x; 4538 break; 4539 } 4540 case 'M': { 4541 $this->y = $y + round($h/2); 4542 $this->x = $this->img_rb_x; 4543 break; 4544 } 4545 case 'B': { 4546 $this->y = $this->img_rb_y; 4547 $this->x = $this->img_rb_x; 4548 break; 4549 } 4550 case 'N': { 4551 $this->SetY($this->img_rb_y); 4552 break; 4553 } 4554 default:{ 4555 break; 4556 } 4557 } 4558 $this->endlinex = $this->img_rb_x; 4559 return $info['i']; 4560 } 4561 4562 /** 4563 * Convert the loaded php image to a JPEG and then return a structure for the PDF creator. 4564 * This function requires GD library and write access to the directory defined on K_PATH_CACHE constant. 4565 * @param string $file Image file name. 4566 * @param image $image Image object. 4567 * return image JPEG image object. 4568 * @access protected 4569 */ 4570 protected function _toJPEG($image) { 4571 $tempname = tempnam(K_PATH_CACHE, 'jpg_'); 4572 imagejpeg($image, $tempname, $this->jpeg_quality); 4573 imagedestroy($image); 4574 $retvars = $this->_parsejpeg($tempname); 4575 // tidy up by removing temporary image 4576 unlink($tempname); 4577 return $retvars; 4578 } 4579 4580 /** 4581 * Extract info from a JPEG file without using the GD library. 4582 * @param string $file image file to parse 4583 * @return array structure containing the image data 4584 * @access protected 4585 */ 4586 protected function _parsejpeg($file) { 4587 $a = getimagesize($file); 4588 if (empty($a)) { 4589 $this->Error('Missing or incorrect image file: '.$file); 4590 } 4591 if ($a[2] != 2) { 4592 $this->Error('Not a JPEG file: '.$file); 4593 } 4594 if ((!isset($a['channels'])) OR ($a['channels'] == 3)) { 4595 $colspace = 'DeviceRGB'; 4596 } elseif ($a['channels'] == 4) { 4597 $colspace = 'DeviceCMYK'; 4598 } else { 4599 $colspace = 'DeviceGray'; 4600 } 4601 $bpc = isset($a['bits']) ? $a['bits'] : 8; 4602 $data = file_get_contents($file); 4603 return array('w' => $a[0], 'h' => $a[1], 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data); 4604 } 4605 4606 /** 4607 * Extract info from a PNG file without using the GD library. 4608 * @param string $file image file to parse 4609 * @return array structure containing the image data 4610 * @access protected 4611 */ 4612 protected function _parsepng($file) { 4613 $f = fopen($file, 'rb'); 4614 if ($f === false) { 4615 $this->Error('Can\'t open image file: '.$file); 4616 } 4617 //Check signature 4618 if (fread($f, 8) != chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) { 4619 $this->Error('Not a PNG file: '.$file); 4620 } 4621 //Read header chunk 4622 fread($f, 4); 4623 if (fread($f, 4) != 'IHDR') { 4624 $this->Error('Incorrect PNG file: '.$file); 4625 } 4626 $w = $this->_freadint($f); 4627 $h = $this->_freadint($f); 4628 $bpc = ord(fread($f, 1)); 4629 if ($bpc > 8) { 4630 //$this->Error('16-bit depth not supported: '.$file); 4631 fclose($f); 4632 return false; 4633 } 4634 $ct = ord(fread($f, 1)); 4635 if ($ct == 0) { 4636 $colspace = 'DeviceGray'; 4637 } elseif ($ct == 2) { 4638 $colspace = 'DeviceRGB'; 4639 } elseif ($ct == 3) { 4640 $colspace = 'Indexed'; 4641 } else { 4642 // alpha channel 4643 fclose($f); 4644 return 'pngalpha'; 4645 } 4646 if (ord(fread($f, 1)) != 0) { 4647 //$this->Error('Unknown compression method: '.$file); 4648 fclose($f); 4649 return false; 4650 } 4651 if (ord(fread($f, 1)) != 0) { 4652 //$this->Error('Unknown filter method: '.$file); 4653 fclose($f); 4654 return false; 4655 } 4656 if (ord(fread($f, 1)) != 0) { 4657 //$this->Error('Interlacing not supported: '.$file); 4658 fclose($f); 4659 return false; 4660 } 4661 fread($f, 4); 4662 $parms = '/DecodeParms <</Predictor 15 /Colors '.($ct==2 ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w.'>>'; 4663 //Scan chunks looking for palette, transparency and image data 4664 $pal = ''; 4665 $trns = ''; 4666 $data = ''; 4667 do { 4668 $n = $this->_freadint($f); 4669 $type = fread($f, 4); 4670 if ($type == 'PLTE') { 4671 //Read palette 4672 $pal = $this->rfread($f, $n); 4673 fread($f, 4); 4674 } elseif ($type == 'tRNS') { 4675 //Read transparency info 4676 $t = $this->rfread($f, $n); 4677 if ($ct == 0) { 4678 $trns = array(ord(substr($t, 1, 1))); 4679 } elseif ($ct == 2) { 4680 $trns = array(ord(substr($t, 1, 1)), ord(substr($t, 3, 1)), ord(substr($t, 5, 1))); 4681 } else { 4682 $pos = strpos($t, chr(0)); 4683 if ($pos !== false) { 4684 $trns = array($pos); 4685 } 4686 } 4687 fread($f, 4); 4688 } elseif ($type == 'IDAT') { 4689 //Read image data block 4690 $data .= $this->rfread($f, $n); 4691 fread($f, 4); 4692 } elseif ($type == 'IEND') { 4693 break; 4694 } else { 4695 $this->rfread($f, $n + 4); 4696 } 4697 } while ($n); 4698 if (($colspace == 'Indexed') AND (empty($pal))) { 4699 //$this->Error('Missing palette in '.$file); 4700 fclose($f); 4701 return false; 4702 } 4703 fclose($f); 4704 return array('w' => $w, 'h' => $h, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'parms' => $parms, 'pal' => $pal, 'trns' => $trns, 'data' => $data); 4705 } 4706 4707 /** 4708 * Binary-safe and URL-safe file read. 4709 * 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. 4710 * @param resource $handle 4711 * @param int $length 4712 * @return Returns the read string or FALSE in case of error. 4713 * @author Nicola Asuni 4714 * @access protected 4715 * @since 4.5.027 (2009-03-16) 4716 */ 4717 protected function rfread($handle, $length) { 4718 $data = fread($handle, $length); 4719 if ($data === false) { 4720 return false; 4721 } 4722 $rest = $length - strlen($data); 4723 if ($rest > 0) { 4724 $data .= $this->rfread($handle, $rest); 4725 } 4726 return $data; 4727 } 4728 4729 /** 4730 * Extract info from a PNG image with alpha channel using the GD library. 4731 * @param string $file Name of the file containing the image. 4732 * @param float $x Abscissa of the upper-left corner. 4733 * @param float $y Ordinate of the upper-left corner. 4734 * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated. 4735 * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated. 4736 * @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. 4737 * @param mixed $link URL or identifier returned by AddLink(). 4738 * @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> 4739 * @param boolean $resize If true resize (reduce) the image to fit $w and $h (requires GD library). 4740 * @param int $dpi dot-per-inch resolution used on resize 4741 * @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> 4742 * @author Valentin Schmidt, Nicola Asuni 4743 * @access protected 4744 * @since 4.3.007 (2008-12-04) 4745 * @see Image() 4746 */ 4747 protected function ImagePngAlpha($file, $x='', $y='', $w=0, $h=0, $type='', $link='', $align='', $resize=false, $dpi=300, $palign='') { 4748 // get image size 4749 list($wpx, $hpx) = getimagesize($file); 4750 // generate images 4751 $img = imagecreatefrompng($file); 4752 $imgalpha = imagecreate($wpx, $hpx); 4753 // generate gray scale pallete 4754 for ($c = 0; $c < 256; ++$c) { 4755 ImageColorAllocate($imgalpha, $c, $c, $c); 4756 } 4757 // extract alpha channel 4758 for ($xpx = 0; $xpx < $wpx; ++$xpx) { 4759 for ($ypx = 0; $ypx < $hpx; ++$ypx) { 4760 $colorindex = imagecolorat($img, $xpx, $ypx); 4761 $col = imagecolorsforindex($img, $colorindex); 4762 imagesetpixel($imgalpha, $xpx, $ypx, $this->getGDgamma((127 - $col['alpha']) * 255 / 127)); 4763 } 4764 } 4765 // create temp alpha file 4766 $tempfile_alpha = tempnam(K_PATH_CACHE, 'mska_'); 4767 imagepng($imgalpha, $tempfile_alpha); 4768 imagedestroy($imgalpha); 4769 // extract image without alpha channel 4770 $imgplain = imagecreatetruecolor($wpx, $hpx); 4771 imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx); 4772 // create temp image file 4773 $tempfile_plain = tempnam(K_PATH_CACHE, 'mskp_'); 4774 imagepng($imgplain, $tempfile_plain); 4775 imagedestroy($imgplain); 4776 // embed mask image 4777 $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false); 4778 // embed image, masked with previously embedded mask 4779 $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask); 4780 // remove temp files 4781 unlink($tempfile_alpha); 4782 unlink($tempfile_plain); 4783 } 4784 4785 /** 4786 * Correct the gamma value to be used with GD library 4787 * @param float $v the gamma value to be corrected 4788 * @access protected 4789 * @since 4.3.007 (2008-12-04) 4790 */ 4791 protected function getGDgamma($v) { 4792 return (pow(($v / 255), 2.2) * 255); 4793 } 4794 4795 /** 4796 * Performs a line break. 4797 * The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter. 4798 * @param float $h The height of the break. By default, the value equals the height of the last printed cell. 4799 * @param boolean $cell if true add a cMargin to the x coordinate 4800 * @access public 4801 * @since 1.0 4802 * @see Cell() 4803 */ 4804 public function Ln($h='', $cell=false) { 4805 //Line feed; default value is last cell height 4806 if ($cell) { 4807 $cellmargin = $this->cMargin; 4808 } else { 4809 $cellmargin = 0; 4810 } 4811 if ($this->rtl) { 4812 $this->x = $this->w - $this->rMargin - $cellmargin; 4813 } else { 4814 $this->x = $this->lMargin + $cellmargin; 4815 } 4816 if (is_string($h)) { 4817 $this->y += $this->lasth; 4818 } else { 4819 $this->y += $h; 4820 } 4821 $this->newline = true; 4822 } 4823 4824 /** 4825 * Returns the relative X value of current position. 4826 * The value is relative to the left border for LTR languages and to the right border for RTL languages. 4827 * @return float 4828 * @access public 4829 * @since 1.2 4830 * @see SetX(), GetY(), SetY() 4831 */ 4832 public function GetX() { 4833 //Get x position 4834 if ($this->rtl) { 4835 return ($this->w - $this->x); 4836 } else { 4837 return $this->x; 4838 } 4839 } 4840 4841 /** 4842 * Returns the absolute X value of current position. 4843 * @return float 4844 * @access public 4845 * @since 1.2 4846 * @see SetX(), GetY(), SetY() 4847 */ 4848 public function GetAbsX() { 4849 return $this->x; 4850 } 4851 4852 /** 4853 * Returns the ordinate of the current position. 4854 * @return float 4855 * @access public 4856 * @since 1.0 4857 * @see SetY(), GetX(), SetX() 4858 */ 4859 public function GetY() { 4860 //Get y position 4861 return $this->y; 4862 } 4863 4864 /** 4865 * Defines the abscissa of the current position. 4866 * If the passed value is negative, it is relative to the right of the page (or left if language is RTL). 4867 * @param float $x The value of the abscissa. 4868 * @access public 4869 * @since 1.2 4870 * @see GetX(), GetY(), SetY(), SetXY() 4871 */ 4872 public function SetX($x) { 4873 //Set x position 4874 if ($this->rtl) { 4875 if ($x >= 0) { 4876 $this->x = $this->w - $x; 4877 } else { 4878 $this->x = abs($x); 4879 } 4880 } else { 4881 if ($x >= 0) { 4882 $this->x = $x; 4883 } else { 4884 $this->x = $this->w + $x; 4885 } 4886 } 4887 if ($this->x < 0) { 4888 $this->x = 0; 4889 } 4890 if ($this->x > $this->w) { 4891 $this->x = $this->w; 4892 } 4893 } 4894 4895 /** 4896 * Moves the current abscissa back to the left margin and sets the ordinate. 4897 * If the passed value is negative, it is relative to the bottom of the page. 4898 * @param float $y The value of the ordinate. 4899 * @param bool $resetx if true (default) reset the X position. 4900 * @access public 4901 * @since 1.0 4902 * @see GetX(), GetY(), SetY(), SetXY() 4903 */ 4904 public function SetY($y, $resetx=true) { 4905 if ($resetx) { 4906 //reset x 4907 if ($this->rtl) { 4908 $this->x = $this->w - $this->rMargin; 4909 } else { 4910 $this->x = $this->lMargin; 4911 } 4912 } 4913 if ($y >= 0) { 4914 $this->y = $y; 4915 } else { 4916 $this->y = $this->h + $y; 4917 } 4918 if ($this->y < 0) { 4919 $this->y = 0; 4920 } 4921 if ($this->y > $this->h) { 4922 $this->y = $this->h; 4923 } 4924 } 4925 4926 /** 4927 * Defines the abscissa and ordinate of the current position. 4928 * If the passed values are negative, they are relative respectively to the right and bottom of the page. 4929 * @param float $x The value of the abscissa 4930 * @param float $y The value of the ordinate 4931 * @access public 4932 * @since 1.2 4933 * @see SetX(), SetY() 4934 */ 4935 public function SetXY($x, $y) { 4936 //Set x and y positions 4937 $this->SetY($y); 4938 $this->SetX($x); 4939 } 4940 4941 /** 4942 * Send the document to a given destination: string, local file or browser. 4943 * In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.<br /> 4944 * The method first calls Close() if necessary to terminate the document. 4945 * @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. 4946 * @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 file with the name given by name.</li><li>S: return the document as a string. name is ignored.</li></ul> 4947 * @access public 4948 * @since 1.0 4949 * @see Close() 4950 */ 4951 public function Output($name='doc.pdf', $dest='I') { 4952 //Output PDF to some destination 4953 //Finish document if necessary 4954 if ($this->state < 3) { 4955 $this->Close(); 4956 } 4957 //Normalize parameters 4958 if (is_bool($dest)) { 4959 $dest = $dest ? 'D' : 'F'; 4960 } 4961 $dest = strtoupper($dest); 4962 if ($dest != 'F') { 4963 $name = preg_replace('/[\s]+/', '_', $name); 4964 $name = preg_replace('/[^a-zA-Z0-9_\.-]/', '', $name); 4965 } 4966 if ($this->sign) { 4967 // *** apply digital signature to the document *** 4968 // get the document content 4969 $pdfdoc = $this->getBuffer(); 4970 // remove last newline 4971 $pdfdoc = substr($pdfdoc, 0, -1); 4972 // Remove the original buffer 4973 if (isset($this->diskcache) AND $this->diskcache) { 4974 // remove buffer file from cache 4975 unlink($this->buffer); 4976 } 4977 unset($this->buffer); 4978 // remove filler space 4979 $tmppos = strpos($pdfdoc, '/ByteRange[0 ********** ********** **********]') + 58; 4980 $pdfdoc = substr($pdfdoc, 0, $tmppos).substr($pdfdoc, $tmppos + $this->signature_max_lenght); 4981 // define the ByteRange 4982 $byte_range = array(); 4983 $byte_range[0] = 0; 4984 $byte_range[1] = $tmppos - 1; 4985 $byte_range[2] = $byte_range[1] + $this->signature_max_lenght; 4986 $byte_range[3] = strlen($pdfdoc) - $byte_range[1]; 4987 // replace the ByteRange 4988 $byterange = sprintf('/ByteRange[0 %010u %010u %010u]', $byte_range[1], $byte_range[2], $byte_range[3]); 4989 $pdfdoc = str_replace('/ByteRange[0 ********** ********** **********]', $byterange, $pdfdoc); 4990 // write the document to a temporary folder 4991 $tempdoc = tempnam(K_PATH_CACHE, 'tmppdf_'); 4992 $f = fopen($tempdoc, 'wb'); 4993 if (!$f) { 4994 $this->Error('Unable to create temporary file: '.$tempdoc); 4995 } 4996 $pdfdoc_lenght = strlen($pdfdoc); 4997 fwrite($f, $pdfdoc, $pdfdoc_lenght); 4998 fclose($f); 4999 // get digital signature. 5000 // IS THE FOLLOWING PROCEDURE CORRECT? THE SIGNED DOCUMENTS ARE NOT VALID! 5001 $tempsign = tempnam(K_PATH_CACHE, 'tmpsig_'); 5002 if (empty($this->signature_data['extracerts'])) { 5003 openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED); 5004 } else { 5005 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']); 5006 } 5007 unlink($tempdoc); 5008 // read signature 5009 $signature = file_get_contents($tempsign, false, null, $pdfdoc_lenght); 5010 unlink($tempsign); 5011 // extract signature 5012 $signature = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13)); 5013 $tmparr = explode("\n\n", $signature); 5014 $signature = $tmparr[1]; 5015 unset($tmparr); 5016 // decode signature 5017 $signature = base64_decode(trim($signature)); 5018 // convert signature to hex 5019 $signature = current(unpack('H*', $signature)); 5020 $signature = str_pad($signature, $this->signature_max_lenght, '0'); 5021 // Add signature to the document 5022 $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).$signature.substr($pdfdoc, (0 - $byte_range[3])); 5023 $this->diskcache = false; 5024 $this->buffer = &$pdfdoc; 5025 $this->bufferlen = strlen($pdfdoc); 5026 } 5027 switch($dest) { 5028 case 'I': { 5029 // Send PDF to the standard output 5030 if (ob_get_contents()) { 5031 $this->Error('Some data has already been output, can\'t send PDF file'); 5032 } 5033 if (php_sapi_name() != 'cli') { 5034 //We send to a browser 5035 header('Content-Type: application/pdf'); 5036 if (headers_sent()) { 5037 $this->Error('Some data has already been output to browser, can\'t send PDF file'); 5038 } 5039 header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1 5040 header('Pragma: public'); 5041 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past 5042 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); 5043 header('Content-Length: '.$this->bufferlen); 5044 header('Content-Disposition: inline; filename="'.basename($name).'";'); 5045 } 5046 echo $this->getBuffer(); 5047 break; 5048 } 5049 case 'D': { 5050 // Download PDF as file 5051 if (ob_get_contents()) { 5052 $this->Error('Some data has already been output, can\'t send PDF file'); 5053 } 5054 header('Content-Description: File Transfer'); 5055 if (headers_sent()) { 5056 $this->Error('Some data has already been output to browser, can\'t send PDF file'); 5057 } 5058 header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1 5059 header('Pragma: public'); 5060 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past 5061 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); 5062 // force download dialog 5063 header('Content-Type: application/force-download'); 5064 //crm-now: changed to avoid error 500 with fastcgi 5065 //header('Content-Type: application/octet-stream', false); 5066 //header('Content-Type: application/download', false); 5067 //header('Content-Type: application/pdf', false); 5068 // use the Content-Disposition header to supply a recommended filename 5069 header('Content-Disposition: attachment; filename="'.basename($name).'";'); 5070 header('Content-Transfer-Encoding: binary'); 5071 header('Content-Length: '.$this->bufferlen); 5072 echo $this->getBuffer(); 5073 break; 5074 } 5075 case 'F': { 5076 // Save PDF to a local file 5077 if ($this->diskcache) { 5078 copy($this->buffer, $name); 5079 } else { 5080 $f = fopen($name, 'wb'); 5081 if (!$f) { 5082 $this->Error('Unable to create output file: '.$name); 5083 } 5084 fwrite($f, $this->getBuffer(), $this->bufferlen); 5085 fclose($f); 5086 } 5087 break; 5088 } 5089 case 'S': { 5090 // Returns PDF as a string 5091 return $this->getBuffer(); 5092 } 5093 default: { 5094 $this->Error('Incorrect output destination: '.$dest); 5095 } 5096 } 5097 return ''; 5098 } 5099 5100 /** 5101 * Unset all class variables except the following critical variables: internal_encoding, state, bufferlen, buffer and diskcache. 5102 * @param boolean $destroyall if true destroys all class variables, otherwise preserves critical variables. 5103 * @param boolean $preserve_objcopy if true preserves the objcopy variable 5104 * @access public 5105 * @since 4.5.016 (2009-02-24) 5106 */ 5107 public function _destroy($destroyall=false, $preserve_objcopy=false) { 5108 if ($destroyall AND isset($this->diskcache) AND $this->diskcache AND (!$preserve_objcopy) AND (!$this->empty_string($this->buffer))) { 5109 // remove buffer file from cache 5110 unlink($this->buffer); 5111 } 5112 foreach (array_keys(get_object_vars($this)) as $val) { 5113 if ($destroyall OR ( 5114 ($val != 'internal_encoding') 5115 AND ($val != 'state') 5116 AND ($val != 'bufferlen') 5117 AND ($val != 'buffer') 5118 AND ($val != 'diskcache') 5119 AND ($val != 'sign') 5120 AND ($val != 'signature_data') 5121 AND ($val != 'signature_max_lenght') 5122 )) { 5123 if (!$preserve_objcopy OR ($val != 'objcopy')) { 5124 unset($this->$val); 5125 } 5126 } 5127 } 5128 } 5129 5130 /** 5131 * Check for locale-related bug 5132 * @access protected 5133 */ 5134 protected function _dochecks() { 5135 //Check for locale-related bug 5136 if (1.1 == 1) { 5137 $this->Error('Don\'t alter the locale before including class file'); 5138 } 5139 //Check for decimal separator 5140 if (sprintf('%.1F', 1.0) != '1.0') { 5141 setlocale(LC_NUMERIC, 'C'); 5142 } 5143 } 5144 5145 /** 5146 * Return fonts path 5147 * @return string 5148 * @access protected 5149 */ 5150 protected function _getfontpath() { 5151 if (!defined('K_PATH_FONTS') AND is_dir(dirname(__FILE__).'/fonts')) { 5152 define('K_PATH_FONTS', dirname(__FILE__).'/fonts/'); 5153 } 5154 return defined('K_PATH_FONTS') ? K_PATH_FONTS : ''; 5155 } 5156 5157 /** 5158 * Output pages. 5159 * @access protected 5160 */ 5161 protected function _putpages() { 5162 $nb = $this->numpages; 5163 if (!empty($this->AliasNbPages)) { 5164 $nbs = $this->formatPageNumber($nb); 5165 $nbu = $this->UTF8ToUTF16BE($nbs, false); // replacement for unicode font 5166 $alias_a = $this->_escape($this->AliasNbPages); 5167 $alias_au = $this->_escape('{'.$this->AliasNbPages.'}'); 5168 if ($this->isunicode) { 5169 $alias_b = $this->_escape($this->UTF8ToLatin1($this->AliasNbPages)); 5170 $alias_bu = $this->_escape($this->UTF8ToLatin1('{'.$this->AliasNbPages.'}')); 5171 $alias_c = $this->_escape($this->utf8StrRev($this->AliasNbPages, false, $this->tmprtl)); 5172 $alias_cu = $this->_escape($this->utf8StrRev('{'.$this->AliasNbPages.'}', false, $this->tmprtl)); 5173 } 5174 } 5175 if (!empty($this->AliasNumPage)) { 5176 $alias_pa = $this->_escape($this->AliasNumPage); 5177 $alias_pau = $this->_escape('{'.$this->AliasNumPage.'}'); 5178 if ($this->isunicode) { 5179 $alias_pb = $this->_escape($this->UTF8ToLatin1($this->AliasNumPage)); 5180 $alias_pbu = $this->_escape($this->UTF8ToLatin1('{'.$this->AliasNumPage.'}')); 5181 $alias_pc = $this->_escape($this->utf8StrRev($this->AliasNumPage, false, $this->tmprtl)); 5182 $alias_pcu = $this->_escape($this->utf8StrRev('{'.$this->AliasNumPage.'}', false, $this->tmprtl)); 5183 } 5184 } 5185 $pagegroupnum = 0; 5186 $filter = ($this->compress) ? '/Filter /FlateDecode ' : ''; 5187 for ($n=1; $n <= $nb; ++$n) { 5188 $temppage = $this->getPageBuffer($n); 5189 if (!empty($this->pagegroups)) { 5190 if(isset($this->newpagegroup[$n])) { 5191 $pagegroupnum = 0; 5192 } 5193 ++$pagegroupnum; 5194 foreach ($this->pagegroups as $k => $v) { 5195 // replace total pages group numbers 5196 $vs = $this->formatPageNumber($v); 5197 $vu = $this->UTF8ToUTF16BE($vs, false); 5198 $alias_ga = $this->_escape($k); 5199 $alias_gau = $this->_escape('{'.$k.'}'); 5200 if ($this->isunicode) { 5201 $alias_gb = $this->_escape($this->UTF8ToLatin1($k)); 5202 $alias_gbu = $this->_escape($this->UTF8ToLatin1('{'.$k.'}')); 5203 $alias_gc = $this->_escape($this->utf8StrRev($k, false, $this->tmprtl)); 5204 $alias_gcu = $this->_escape($this->utf8StrRev('{'.$k.'}', false, $this->tmprtl)); 5205 } 5206 $temppage = str_replace($alias_gau, $vu, $temppage); 5207 if ($this->isunicode) { 5208 $temppage = str_replace($alias_gbu, $vu, $temppage); 5209 $temppage = str_replace($alias_gcu, $vu, $temppage); 5210 $temppage = str_replace($alias_gb, $vs, $temppage); 5211 $temppage = str_replace($alias_gc, $vs, $temppage); 5212 } 5213 $temppage = str_replace($alias_ga, $vs, $temppage); 5214 // replace page group numbers 5215 $pvs = $this->formatPageNumber($pagegroupnum); 5216 $pvu = $this->UTF8ToUTF16BE($pvs, false); 5217 $pk = str_replace('{nb', '{pnb', $k); 5218 $alias_pga = $this->_escape($pk); 5219 $alias_pgau = $this->_escape('{'.$pk.'}'); 5220 if ($this->isunicode) { 5221 $alias_pgb = $this->_escape($this->UTF8ToLatin1($pk)); 5222 $alias_pgbu = $this->_escape($this->UTF8ToLatin1('{'.$pk.'}')); 5223 $alias_pgc = $this->_escape($this->utf8StrRev($pk, false, $this->tmprtl)); 5224 $alias_pgcu = $this->_escape($this->utf8StrRev('{'.$pk.'}', false, $this->tmprtl)); 5225 } 5226 $temppage = str_replace($alias_pgau, $pvu, $temppage); 5227 if ($this->isunicode) { 5228 $temppage = str_replace($alias_pgbu, $pvu, $temppage); 5229 $temppage = str_replace($alias_pgcu, $pvu, $temppage); 5230 $temppage = str_replace($alias_pgb, $pvs, $temppage); 5231 $temppage = str_replace($alias_pgc, $pvs, $temppage); 5232 } 5233 $temppage = str_replace($alias_pga, $pvs, $temppage); 5234 } 5235 } 5236 if (!empty($this->AliasNbPages)) { 5237 // replace total pages number 5238 $temppage = str_replace($alias_au, $nbu, $temppage); 5239 if ($this->isunicode) { 5240 $temppage = str_replace($alias_bu, $nbu, $temppage); 5241 $temppage = str_replace($alias_cu, $nbu, $temppage); 5242 $temppage = str_replace($alias_b, $nbs, $temppage); 5243 $temppage = str_replace($alias_c, $nbs, $temppage); 5244 } 5245 $temppage = str_replace($alias_a, $nbs, $temppage); 5246 } 5247 if (!empty($this->AliasNumPage)) { 5248 // replace page number 5249 $pnbs = $this->formatPageNumber($n); 5250 $pnbu = $this->UTF8ToUTF16BE($pnbs, false); // replacement for unicode font 5251 $temppage = str_replace($alias_pau, $pnbu, $temppage); 5252 if ($this->isunicode) { 5253 $temppage = str_replace($alias_pbu, $pnbu, $temppage); 5254 $temppage = str_replace($alias_pcu, $pnbu, $temppage); 5255 $temppage = str_replace($alias_pb, $pnbs, $temppage); 5256 $temppage = str_replace($alias_pc, $pnbs, $temppage); 5257 } 5258 $temppage = str_replace($alias_pa, $pnbs, $temppage); 5259 } 5260 $temppage = str_replace($this->epsmarker, '', $temppage); 5261 //$this->setPageBuffer($n, $temppage); 5262 //Page 5263 $this->_newobj(); 5264 $this->_out('<</Type /Page'); 5265 $this->_out('/Parent 1 0 R'); 5266 $this->_out(sprintf('/MediaBox [0 0 %.2F %.2F]', $this->pagedim[$n]['w'], $this->pagedim[$n]['h'])); 5267 $this->_out('/Resources 2 0 R'); 5268 $this->_putannots($n); 5269 $this->_out('/Contents '.($this->n + 1).' 0 R>>'); 5270 $this->_out('endobj'); 5271 //Page content 5272 $p = ($this->compress) ? gzcompress($temppage) : $temppage; 5273 $this->_newobj(); 5274 $this->_out('<<'.$filter.'/Length '.strlen($p).'>>'); 5275 $this->_putstream($p); 5276 $this->_out('endobj'); 5277 if ($this->diskcache) { 5278 // remove temporary files 5279 unlink($this->pages[$n]); 5280 } 5281 } 5282 //Pages root 5283 $this->offsets[1] = $this->bufferlen; 5284 $this->_out('1 0 obj'); 5285 $this->_out('<</Type /Pages'); 5286 $kids='/Kids ['; 5287 for ($i=0; $i < $nb; ++$i) { 5288 $kids .= (3 + (2 * $i)).' 0 R '; 5289 } 5290 $this->_out($kids.']'); 5291 $this->_out('/Count '.$nb); 5292 //$this->_out(sprintf('/MediaBox [0 0 %.2F %.2F]',$this->pagedim[0]['w'],$this->pagedim[0]['h'])); 5293 $this->_out('>>'); 5294 $this->_out('endobj'); 5295 } 5296 5297 /** 5298 * Output Page Annotations. 5299 * !!! THIS FUNCTION IS NOT YET COMPLETED !!! 5300 * See section 8.4 of PDF reference. 5301 * @param int $n page number 5302 * @access protected 5303 * @author Nicola Asuni 5304 * @since 4.0.018 (2008-08-06) 5305 */ 5306 protected function _putannots($n) { 5307 if (isset($this->PageAnnots[$n])) { 5308 $annots = '/Annots ['; 5309 foreach ($this->PageAnnots[$n] as $key => $pl) { 5310 $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER); 5311 $a = $pl['x'] * $this->k; 5312 $b = $this->pagedim[$n]['h'] - ($pl['y'] * $this->k); 5313 $c = $pl['w'] * $this->k; 5314 $d = $pl['h'] * $this->k; 5315 $rect = sprintf('%.2F %.2F %.2F %.2F', $a, $b, $a+$c, $b-$d); 5316 $annots .= "\n"; 5317 $annots .= '<</Type /Annot'; 5318 $annots .= ' /Subtype /'.$pl['opt']['subtype']; 5319 $annots .= ' /Rect ['.$rect.']'; 5320 $annots .= ' /Contents '.$this->_textstring($pl['txt']); 5321 //$annots .= ' /P '; 5322 $annots .= ' /NM '.$this->_textstring(sprintf('%04u-%04u', $n, $key)); 5323 $annots .= ' /M '.$this->_datastring('D:'.date('YmdHis')); 5324 if (isset($pl['opt']['f'])) { 5325 $val = 0; 5326 if (is_array($pl['opt']['f'])) { 5327 foreach ($pl['opt']['f'] as $f) { 5328 switch (strtolower($f)) { 5329 case 'invisible': { 5330 $val += 1 << 0; 5331 break; 5332 } 5333 case 'hidden': { 5334 $val += 1 << 1; 5335 break; 5336 } 5337 case 'print': { 5338 $val += 1 << 2; 5339 break; 5340 } 5341 case 'nozoom': { 5342 $val += 1 << 3; 5343 break; 5344 } 5345 case 'norotate': { 5346 $val += 1 << 4; 5347 break; 5348 } 5349 case 'noview': { 5350 $val += 1 << 5; 5351 break; 5352 } 5353 case 'readonly': { 5354 $val += 1 << 6; 5355 break; 5356 } 5357 case 'locked': { 5358 $val += 1 << 8; 5359 break; 5360 } 5361 case 'togglenoview': { 5362 $val += 1 << 9; 5363 break; 5364 } 5365 case 'lockedcontents': { 5366 $val += 1 << 10; 5367 break; 5368 } 5369 default: { 5370 break; 5371 } 5372 } 5373 } 5374 } 5375 $annots .= ' /F '.intval($val); 5376 } 5377 //$annots .= ' /AP '; 5378 //$annots .= ' /AS '; 5379 $annots .= ' /Border ['; 5380 if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) { 5381 $annots .= intval($pl['opt']['border'][0]).' '; 5382 $annots .= intval($pl['opt']['border'][1]).' '; 5383 $annots .= intval($pl['opt']['border'][2]); 5384 if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) { 5385 $annots .= ' ['; 5386 foreach ($pl['opt']['border'][3] as $dash) { 5387 $annots .= intval($dash).' '; 5388 } 5389 $annots .= ']'; 5390 } 5391 } else { 5392 $annots .= '0 0 0'; 5393 } 5394 $annots .= ']'; 5395 if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) { 5396 $annots .= ' /BS <<Type /Border'; 5397 if (isset($pl['opt']['bs']['w'])) { 5398 $annots .= ' /W '.sprintf("%.4F", floatval($pl['opt']['bs']['w'])); 5399 } 5400 $bstyles = array('S', 'D', 'B', 'I', 'U'); 5401 if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $bstyles)) { 5402 $annots .= ' /S /'.$pl['opt']['bs']['s']; 5403 } 5404 if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) { 5405 $annots .= ' /D ['; 5406 foreach ($pl['opt']['bs']['d'] as $cord) { 5407 $cord = floatval($cord); 5408 $annots .= sprintf(" %.4F", $cord); 5409 } 5410 $annots .= ']'; 5411 } 5412 $annots .= '>> '; 5413 } 5414 if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) { 5415 $annots .= ' /BE <<'; 5416 $bstyles = array('S', 'C'); 5417 if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $markups)) { 5418 $annots .= ' /S /'.$pl['opt']['bs']['s']; 5419 } else { 5420 $annots .= ' /S /S'; 5421 } 5422 if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) { 5423 $annots .= ' /I '.sprintf(" %.4F", $pl['opt']['be']['i']); 5424 } 5425 $annots .= '>>'; 5426 } 5427 $annots .= ' /C ['; 5428 if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c']))) { 5429 foreach ($pl['opt']['c'] as $col) { 5430 $col = intval($col); 5431 $color = $col <= 0 ? 0 : ($col >= 255 ? 1 : $col / 255); 5432 $annots .= sprintf(" %.4F", $color); 5433 } 5434 } 5435 $annots .= ']'; 5436 //$annots .= ' /StructParent '; 5437 //$annots .= ' /OC '; 5438 $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight', 'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound'); 5439 if (in_array(strtolower($pl['opt']['subtype']), $markups)) { 5440 // this is a markup type 5441 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) { 5442 $annots .= ' /T '.$this->_textstring($pl['opt']['t']); 5443 } 5444 //$annots .= ' /Popup '; 5445 if (isset($pl['opt']['ca'])) { 5446 $annots .= ' /CA '.sprintf("%.4F", floatval($pl['opt']['ca'])); 5447 } 5448 if (isset($pl['opt']['rc'])) { 5449 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc']); 5450 } 5451 $annots .= ' /CreationDate '.$this->_datastring('D:'.date('YmdHis')); 5452 //$annots .= ' /IRT '; 5453 if (isset($pl['opt']['subj'])) { 5454 $annots .= ' /Subj '.$this->_textstring($pl['opt']['subj']); 5455 } 5456 //$annots .= ' /RT '; 5457 //$annots .= ' /IT '; 5458 //$annots .= ' /ExData '; 5459 } 5460 switch (strtolower($pl['opt']['subtype'])) { 5461 case 'text': { 5462 if (isset($pl['opt']['open'])) { 5463 $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false'); 5464 } 5465 $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph'); 5466 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) { 5467 $annots .= ' /Name /'.$pl['opt']['name']; 5468 } else { 5469 $annots .= ' /Name /Note'; 5470 } 5471 $statemodels = array('Marked', 'Review'); 5472 if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) { 5473 $annots .= ' /StateModel /'.$pl['opt']['statemodel']; 5474 } else { 5475 $pl['opt']['statemodel'] = 'Marked'; 5476 $annots .= ' /StateModel /'.$pl['opt']['statemodel']; 5477 } 5478 if ($pl['opt']['statemodel'] == 'Marked') { 5479 $states = array('Accepted', 'Unmarked'); 5480 } else { 5481 $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None'); 5482 } 5483 if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) { 5484 $annots .= ' /State /'.$pl['opt']['state']; 5485 } else { 5486 if ($pl['opt']['statemodel'] == 'Marked') { 5487 $annots .= ' /State /Unmarked'; 5488 } else { 5489 $annots .= ' /State /None'; 5490 } 5491 } 5492 break; 5493 } 5494 case 'link': { 5495 if(is_string($pl['txt'])) { 5496 // external URI link 5497 $annots .= ' /A <</S /URI /URI '.$this->_datastring($pl['txt']).'>>'; 5498 } else { 5499 // internal link 5500 $l = $this->links[$pl['txt']]; 5501 $annots .= sprintf(' /Dest [%d 0 R /XYZ 0 %.2F null]', (1 + (2 * $l[0])), ($this->pagedim[$l[0]]['h'] - ($l[1] * $this->k))); 5502 } 5503 $hmodes = array('N', 'I', 'O', 'P'); 5504 if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) { 5505 $annots .= ' /H /'.$pl['opt']['h']; 5506 } else { 5507 $annots .= ' /H /I'; 5508 } 5509 //$annots .= ' /PA '; 5510 //$annots .= ' /Quadpoints '; 5511 break; 5512 } 5513 case 'freetext': { 5514 $annots .= ' /DA '.$this->_textstring($pl['txt']); 5515 if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) { 5516 $annots .= ' /Q '.intval($pl['opt']['q']); 5517 } 5518 if (isset($pl['opt']['rc'])) { 5519 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc']); 5520 } 5521 if (isset($pl['opt']['ds'])) { 5522 $annots .= ' /DS '.$this->_textstring($pl['opt']['ds']); 5523 } 5524 if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) { 5525 $annots .= ' /CL ['; 5526 foreach ($pl['opt']['cl'] as $cl) { 5527 $annots .= sprintf("%.4F ", $cl * $this->k); 5528 } 5529 $annots .= ']'; 5530 } 5531 $tfit = array('FreeTextCallout', 'FreeTextTypeWriter'); 5532 if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) { 5533 $annots .= ' /IT '.$pl['opt']['it']; 5534 } 5535 if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) { 5536 $l = $pl['opt']['rd'][0] * $this->k; 5537 $r = $pl['opt']['rd'][1] * $this->k; 5538 $t = $pl['opt']['rd'][2] * $this->k; 5539 $b = $pl['opt']['rd'][3] * $this->k; 5540 $annots .= ' /RD ['.sprintf('%.2F %.2F %.2F %.2F', $l, $r, $t, $b).']'; 5541 } 5542 //$annots .= ' /LE '; 5543 break; 5544 } 5545 // ... to be completed ... 5546 case 'line': { 5547 break; 5548 } 5549 case 'square': { 5550 break; 5551 } 5552 case 'circle': { 5553 break; 5554 } 5555 case 'polygon': { 5556 break; 5557 } 5558 case 'polyline': { 5559 break; 5560 } 5561 case 'highlight': { 5562 break; 5563 } 5564 case 'underline': { 5565 break; 5566 } 5567 case 'squiggly': { 5568 break; 5569 } 5570 case 'strikeout': { 5571 break; 5572 } 5573 case 'stamp': { 5574 break; 5575 } 5576 case 'caret': { 5577 break; 5578 } 5579 case 'ink': { 5580 break; 5581 } 5582 case 'popup': { 5583 break; 5584 } 5585 case 'fileattachment': { 5586 if (!isset($pl['opt']['fs'])) { 5587 break; 5588 } 5589 $filename = basename($pl['opt']['fs']); 5590 if (isset($this->embeddedfiles[$filename]['n'])) { 5591 $annots .= ' /FS <</Type /Filespec /F '.$this->_datastring($filename).' /EF <</F '.$this->embeddedfiles[$filename]['n'].' 0 R>> >>'; 5592 $iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag'); 5593 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) { 5594 $annots .= ' /Name /'.$pl['opt']['name']; 5595 } else { 5596 $annots .= ' /Name /PushPin'; 5597 } 5598 } 5599 break; 5600 } 5601 case 'sound': { 5602 if (!isset($pl['opt']['sound'])) { 5603 break; 5604 } 5605 $filename = basename($pl['opt']['sound']); 5606 if (isset($this->embeddedfiles[$filename]['n'])) { 5607 // ... TO BE COMPLETED ... 5608 $iconsapp = array('Speaker', 'Mic'); 5609 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) { 5610 $annots .= ' /Name /'.$pl['opt']['name']; 5611 } else { 5612 $annots .= ' /Name /Speaker'; 5613 } 5614 } 5615 break; 5616 } 5617 case 'movie': { 5618 break; 5619 } 5620 case 'widget': { 5621 if (isset($pl['opt']['h'])) { 5622 $annots .= ' /H '.intval($pl['opt']['h']); 5623 } 5624 if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk']))) { 5625 $annots .= ' /MK <<'; 5626 // ... TO BE COMPLETED ... 5627 $annots .= '>>'; 5628 } 5629 break; 5630 } 5631 case 'screen': { 5632 break; 5633 } 5634 case 'printermark': { 5635 break; 5636 } 5637 case 'trapnet': { 5638 break; 5639 } 5640 case 'watermark': { 5641 break; 5642 } 5643 case '3d': { 5644 break; 5645 } 5646 default: { 5647 break; 5648 } 5649 } 5650 5651 $annots .= '>>'; 5652 } 5653 $annots .= "\n]"; 5654 $this->_out($annots); 5655 } 5656 } 5657 5658 /** 5659 * Output fonts. 5660 * @access protected 5661 */ 5662 protected function _putfonts() { 5663 $nf = $this->n; 5664 foreach ($this->diffs as $diff) { 5665 //Encodings 5666 $this->_newobj(); 5667 $this->_out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.']>>'); 5668 $this->_out('endobj'); 5669 } 5670 $mqr = get_magic_quotes_runtime(); 5671 set_magic_quotes_runtime(0); 5672 foreach ($this->FontFiles as $file => $info) { 5673 // search and get font file to embedd 5674 $fontdir = $info['fontdir']; 5675 $file = strtolower($file); 5676 $fontfile = ''; 5677 // search files on various directories 5678 if (file_exists($fontdir.$file)) { 5679 $fontfile = $fontdir.$file; 5680 } elseif (file_exists($this->_getfontpath().$file)) { 5681 $fontfile = $this->_getfontpath().$file; 5682 } elseif (file_exists($file)) { 5683 $fontfile = $file; 5684 } 5685 if (!$this->empty_string($fontfile)) { 5686 $font = file_get_contents($fontfile); 5687 $compressed = (substr($file, -2) == '.z'); 5688 if ((!$compressed) AND (isset($info['length2']))) { 5689 $header = (ord($font{0}) == 128); 5690 if ($header) { 5691 //Strip first binary header 5692 $font = substr($font, 6); 5693 } 5694 if ($header AND (ord($font{$info['length1']}) == 128)) { 5695 //Strip second binary header 5696 $font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] + 6)); 5697 } 5698 } 5699 $this->_newobj(); 5700 $this->FontFiles[$file]['n'] = $this->n; 5701 $this->_out('<</Length '.strlen($font)); 5702 if ($compressed) { 5703 $this->_out('/Filter /FlateDecode'); 5704 } 5705 $this->_out('/Length1 '.$info['length1']); 5706 if (isset($info['length2'])) { 5707 $this->_out('/Length2 '.$info['length2'].' /Length3 0'); 5708 } 5709 $this->_out('>>'); 5710 $this->_putstream($font); 5711 $this->_out('endobj'); 5712 } 5713 } 5714 set_magic_quotes_runtime($mqr); 5715 foreach ($this->fontkeys as $k) { 5716 //Font objects 5717 $this->setFontSubBuffer($k, 'n', $this->n + 1); 5718 $font = $this->getFontBuffer($k); 5719 $type = $font['type']; 5720 $name = $font['name']; 5721 if ($type == 'core') { 5722 //Standard font 5723 $this->_newobj(); 5724 $this->_out('<</Type /Font'); 5725 $this->_out('/BaseFont /'.$name); 5726 $this->_out('/Subtype /Type1'); 5727 if (($name != 'symbol') AND ($name != 'zapfdingbats')) { 5728 $this->_out('/Encoding /WinAnsiEncoding'); 5729 } 5730 $this->_out('>>'); 5731 $this->_out('endobj'); 5732 } elseif (($type == 'Type1') OR ($type == 'TrueType')) { 5733 //Additional Type1 or TrueType font 5734 $this->_newobj(); 5735 $this->_out('<</Type /Font'); 5736 $this->_out('/BaseFont /'.$name); 5737 $this->_out('/Subtype /'.$type); 5738 $this->_out('/FirstChar 32 /LastChar 255'); 5739 $this->_out('/Widths '.($this->n + 1).' 0 R'); 5740 $this->_out('/FontDescriptor '.($this->n + 2).' 0 R'); 5741 if ($font['enc']) { 5742 if (isset($font['diff'])) { 5743 $this->_out('/Encoding '.($nf + $font['diff']).' 0 R'); 5744 } else { 5745 $this->_out('/Encoding /WinAnsiEncoding'); 5746 } 5747 } 5748 $this->_out('>>'); 5749 $this->_out('endobj'); 5750 // Widths 5751 $this->_newobj(); 5752 $cw = &$font['cw']; 5753 $s = '['; 5754 for ($i = 32; $i < 256; ++$i) { 5755 $s .= $cw[$i].' '; 5756 } 5757 $this->_out($s.']'); 5758 $this->_out('endobj'); 5759 //Descriptor 5760 $this->_newobj(); 5761 $s = '<</Type /FontDescriptor /FontName /'.$name; 5762 foreach ($font['desc'] as $k => $v) { 5763 $s .= ' /'.$k.' '.$v.''; 5764 } 5765 if (!$this->empty_string($font['file'])) { 5766 $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R'; 5767 } 5768 $this->_out($s.'>>'); 5769 $this->_out('endobj'); 5770 } else { 5771 //Allow for additional types 5772 $mtd = '_put'.strtolower($type); 5773 if (!method_exists($this, $mtd)) { 5774 $this->Error('Unsupported font type: '.$type); 5775 } 5776 $this->$mtd($font); 5777 } 5778 } 5779 } 5780 5781 /** 5782 * Outputs font widths 5783 * @parameter array $font font data 5784 * @parameter int $cidoffset offset for CID values 5785 * @author Nicola Asuni 5786 * @access protected 5787 * @since 4.4.000 (2008-12-07) 5788 */ 5789 protected function _putfontwidths($font, $cidoffset=0) { 5790 ksort($font['cw']); 5791 $rangeid = 0; 5792 $range = array(); 5793 $prevcid = -2; 5794 $prevwidth = -1; 5795 $interval = false; 5796 // for each character 5797 foreach ($font['cw'] as $cid => $width) { 5798 $cid -= $cidoffset; 5799 if ($width != $font['dw']) { 5800 if ($cid == ($prevcid + 1)) { 5801 // consecutive CID 5802 if ($width == $prevwidth) { 5803 if ($width == $range[$rangeid][0]) { 5804 $range[$rangeid][] = $width; 5805 } else { 5806 array_pop($range[$rangeid]); 5807 // new range 5808 $rangeid = $prevcid; 5809 $range[$rangeid] = array(); 5810 $range[$rangeid][] = $prevwidth; 5811 $range[$rangeid][] = $width; 5812 } 5813 $interval = true; 5814 $range[$rangeid]['interval'] = true; 5815 } else { 5816 if ($interval) { 5817 // new range 5818 $rangeid = $cid; 5819 $range[$rangeid] = array(); 5820 $range[$rangeid][] = $width; 5821 } else { 5822 $range[$rangeid][] = $width; 5823 } 5824 $interval = false; 5825 } 5826 } else { 5827 // new range 5828 $rangeid = $cid; 5829 $range[$rangeid] = array(); 5830 $range[$rangeid][] = $width; 5831 $interval = false; 5832 } 5833 $prevcid = $cid; 5834 $prevwidth = $width; 5835 } 5836 } 5837 // optimize ranges 5838 $prevk = -1; 5839 $nextk = -1; 5840 $prevint = false; 5841 foreach ($range as $k => $ws) { 5842 $cws = count($ws); 5843 if (($k == $nextk) AND (!$prevint) AND ((!isset($ws['interval'])) OR ($cws < 4))) { 5844 if (isset($range[$k]['interval'])) { 5845 unset($range[$k]['interval']); 5846 } 5847 $range[$prevk] = array_merge($range[$prevk], $range[$k]); 5848 unset($range[$k]); 5849 } else { 5850 $prevk = $k; 5851 } 5852 $nextk = $k + $cws; 5853 if (isset($ws['interval'])) { 5854 if ($cws > 3) { 5855 $prevint = true; 5856 } else { 5857 $prevint = false; 5858 } 5859 unset($range[$k]['interval']); 5860 --$nextk; 5861 } else { 5862 $prevint = false; 5863 } 5864 } 5865 // output data 5866 $w = ''; 5867 foreach ($range as $k => $ws) { 5868 if (count(array_count_values($ws)) == 1) { 5869 // interval mode is more compact 5870 $w .= ' '.$k.' '.($k + count($ws) - 1).' '.$ws[0]; 5871 } else { 5872 // range mode 5873 $w .= ' '.$k.' [ '.implode(' ', $ws).' ]'; 5874 } 5875 } 5876 $this->_out('/W ['.$w.' ]'); 5877 } 5878 5879 /** 5880 * Adds unicode fonts.<br> 5881 * Based on PDF Reference 1.3 (section 5) 5882 * @parameter array $font font data 5883 * @access protected 5884 * @author Nicola Asuni 5885 * @since 1.52.0.TC005 (2005-01-05) 5886 */ 5887 protected function _puttruetypeunicode($font) { 5888 // Type0 Font 5889 // A composite font composed of other fonts, organized hierarchically 5890 $this->_newobj(); 5891 $this->_out('<</Type /Font'); 5892 $this->_out('/Subtype /Type0'); 5893 $this->_out('/BaseFont /'.$font['name'].''); 5894 $this->_out('/Encoding /Identity-H'); //The horizontal identity mapping for 2-byte CIDs; may be used with CIDFonts using any Registry, Ordering, and Supplement values. 5895 $this->_out('/ToUnicode /Identity-H'); 5896 $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]'); 5897 $this->_out('>>'); 5898 $this->_out('endobj'); 5899 // CIDFontType2 5900 // A CIDFont whose glyph descriptions are based on TrueType font technology 5901 $this->_newobj(); 5902 $this->_out('<</Type /Font'); 5903 $this->_out('/Subtype /CIDFontType2'); 5904 $this->_out('/BaseFont /'.$font['name'].''); 5905 // A dictionary containing entries that define the character collection of the CIDFont. 5906 $cidinfo = '/Registry '.$this->_datastring('Adobe'); 5907 $cidinfo .= ' /Ordering '.$this->_datastring('Identity'); 5908 $cidinfo .= ' /Supplement 0'; 5909 $this->_out('/CIDSystemInfo <<'.$cidinfo.'>>'); 5910 $this->_out('/FontDescriptor '.($this->n + 1).' 0 R'); 5911 $this->_out('/DW '.$font['dw'].''); // default width 5912 $this->_putfontwidths($font, 0); 5913 $this->_out('/CIDToGIDMap '.($this->n + 2).' 0 R'); 5914 $this->_out('>>'); 5915 $this->_out('endobj'); 5916 // Font descriptor 5917 // A font descriptor describing the CIDFont default metrics other than its glyph widths 5918 $this->_newobj(); 5919 $this->_out('<</Type /FontDescriptor'); 5920 $this->_out('/FontName /'.$font['name']); 5921 foreach ($font['desc'] as $key => $value) { 5922 $this->_out('/'.$key.' '.$value); 5923 } 5924 $fontdir = ''; 5925 if (!$this->empty_string($font['file'])) { 5926 // A stream containing a TrueType font 5927 $this->_out('/FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R'); 5928 $fontdir = $this->FontFiles[$font['file']]['fontdir']; 5929 } 5930 $this->_out('>>'); 5931 $this->_out('endobj'); 5932 $this->_newobj(); 5933 if (isset($font['ctg']) AND (!$this->empty_string($font['ctg']))) { 5934 // Embed CIDToGIDMap 5935 // A specification of the mapping from CIDs to glyph indices 5936 // search and get CTG font file to embedd 5937 $ctgfile = strtolower($font['ctg']); 5938 // search and get ctg font file to embedd 5939 $fontfile = ''; 5940 // search files on various directories 5941 if (file_exists($fontdir.$ctgfile)) { 5942 $fontfile = $fontdir.$ctgfile; 5943 } elseif (file_exists($this->_getfontpath().$ctgfile)) { 5944 $fontfile = $this->_getfontpath().$ctgfile; 5945 } elseif (file_exists($ctgfile)) { 5946 $fontfile = $ctgfile; 5947 } 5948 if ($this->empty_string($fontfile)) { 5949 $this->Error('Font file not found: '.$ctgfile); 5950 } 5951 $size = filesize($fontfile); 5952 $this->_out('<</Length '.$size.''); 5953 if (substr($fontfile, -2) == '.z') { // check file extension 5954 // Decompresses data encoded using the public-domain 5955 // zlib/deflate compression method, reproducing the 5956 // original text or binary data 5957 $this->_out('/Filter /FlateDecode'); 5958 } 5959 $this->_out('>>'); 5960 $this->_putstream(file_get_contents($fontfile)); 5961 } 5962 $this->_out('endobj'); 5963 } 5964 5965 /** 5966 * Output CID-0 fonts. 5967 * @param array $font font data 5968 * @access protected 5969 * @author Andrew Whitehead, Nicola Asuni, Yukihiro Nakadaira 5970 * @since 3.2.000 (2008-06-23) 5971 */ 5972 protected function _putcidfont0($font) { 5973 $cidoffset = 31; 5974 if (isset($font['cidinfo']['uni2cid'])) { 5975 // convert unicode to cid. 5976 $uni2cid = $font['cidinfo']['uni2cid']; 5977 $cw = array(); 5978 foreach ($font['cw'] as $uni => $width) { 5979 if (isset($uni2cid[$uni])) { 5980 $cw[($uni2cid[$uni] + $cidoffset)] = $width; 5981 } elseif ($uni < 256) { 5982 $cw[$uni] = $width; 5983 } // else unknown character 5984 } 5985 $font = array_merge($font, array('cw' => $cw)); 5986 } 5987 $name = $font['name']; 5988 $enc = $font['enc']; 5989 if ($enc) { 5990 $longname = $name.'-'.$enc; 5991 } else { 5992 $longname = $name; 5993 } 5994 $this->_newobj(); 5995 $this->_out('<</Type /Font'); 5996 $this->_out('/BaseFont /'.$longname); 5997 $this->_out('/Subtype /Type0'); 5998 if ($enc) { 5999 $this->_out('/Encoding /'.$enc); 6000 } 6001 $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]'); 6002 $this->_out('>>'); 6003 $this->_out('endobj'); 6004 $this->_newobj(); 6005 $this->_out('<</Type /Font'); 6006 $this->_out('/BaseFont /'.$name); 6007 $this->_out('/Subtype /CIDFontType0'); 6008 $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry']); 6009 $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering']); 6010 $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement']; 6011 $this->_out('/CIDSystemInfo <<'.$cidinfo.'>>'); 6012 $this->_out('/FontDescriptor '.($this->n + 1).' 0 R'); 6013 $this->_out('/DW '.$font['dw']); 6014 $this->_putfontwidths($font, $cidoffset); 6015 $this->_out('>>'); 6016 $this->_out('endobj'); 6017 $this->_newobj(); 6018 $s = '<</Type /FontDescriptor /FontName /'.$name; 6019 foreach ($font['desc'] as $k => $v) { 6020 if ($k != 'Style') { 6021 $s .= ' /'.$k.' '.$v.''; 6022 } 6023 } 6024 $this->_out($s.'>>'); 6025 $this->_out('endobj'); 6026 } 6027 6028 /** 6029 * Output images. 6030 * @access protected 6031 */ 6032 protected function _putimages() { 6033 $filter = ($this->compress) ? '/Filter /FlateDecode ' : ''; 6034 foreach ($this->imagekeys as $file) { 6035 $info = $this->getImageBuffer($file); 6036 $this->_newobj(); 6037 $this->setImageSubBuffer($file, 'n', $this->n); 6038 $this->_out('<</Type /XObject'); 6039 $this->_out('/Subtype /Image'); 6040 $this->_out('/Width '.$info['w']); 6041 $this->_out('/Height '.$info['h']); 6042 if (isset($info['masked'])) { 6043 $this->_out('/SMask '.($this->n - 1).' 0 R'); 6044 } 6045 if ($info['cs'] == 'Indexed') { 6046 $this->_out('/ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n + 1).' 0 R]'); 6047 } else { 6048 $this->_out('/ColorSpace /'.$info['cs']); 6049 if ($info['cs'] == 'DeviceCMYK') { 6050 $this->_out('/Decode [1 0 1 0 1 0 1 0]'); 6051 } 6052 } 6053 $this->_out('/BitsPerComponent '.$info['bpc']); 6054 if (isset($info['f'])) { 6055 $this->_out('/Filter /'.$info['f']); 6056 } 6057 if (isset($info['parms'])) { 6058 $this->_out($info['parms']); 6059 } 6060 if (isset($info['trns']) AND is_array($info['trns'])) { 6061 $trns=''; 6062 $count_info = count($info['trns']); 6063 for ($i=0; $i < $count_info; ++$i) { 6064 $trns .= $info['trns'][$i].' '.$info['trns'][$i].' '; 6065 } 6066 $this->_out('/Mask ['.$trns.']'); 6067 } 6068 $this->_out('/Length '.strlen($info['data']).'>>'); 6069 $this->_putstream($info['data']); 6070 $this->_out('endobj'); 6071 //Palette 6072 if ($info['cs'] == 'Indexed') { 6073 $this->_newobj(); 6074 $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal']; 6075 $this->_out('<<'.$filter.'/Length '.strlen($pal).'>>'); 6076 $this->_putstream($pal); 6077 $this->_out('endobj'); 6078 } 6079 } 6080 } 6081 6082 /** 6083 * Output Spot Colors Resources. 6084 * @access protected 6085 * @since 4.0.024 (2008-09-12) 6086 */ 6087 protected function _putspotcolors() { 6088 foreach ($this->spot_colors as $name => $color) { 6089 $this->_newobj(); 6090 $this->spot_colors[$name]['n'] = $this->n; 6091 $this->_out('[/Separation /'.str_replace(' ', '#20', $name)); 6092 $this->_out('/DeviceCMYK <<'); 6093 $this->_out('/Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0] '); 6094 $this->_out(sprintf('/C1 [%.4F %.4F %.4F %.4F] ', $color['c']/100, $color['m']/100, $color['y']/100, $color['k']/100)); 6095 $this->_out('/FunctionType 2 /Domain [0 1] /N 1>>]'); 6096 $this->_out('endobj'); 6097 } 6098 } 6099 6100 /** 6101 * Output object dictionary for images. 6102 * @access protected 6103 */ 6104 protected function _putxobjectdict() { 6105 foreach ($this->imagekeys as $file) { 6106 $info = $this->getImageBuffer($file); 6107 $this->_out('/I'.$info['i'].' '.$info['n'].' 0 R'); 6108 } 6109 } 6110 6111 /** 6112 * Output Resources Dictionary. 6113 * @access protected 6114 */ 6115 protected function _putresourcedict() { 6116 $this->_out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'); 6117 $this->_out('/Font <<'); 6118 foreach ($this->fontkeys as $fontkey) { 6119 $font = $this->getFontBuffer($fontkey); 6120 $this->_out('/F'.$font['i'].' '.$font['n'].' 0 R'); 6121 } 6122 $this->_out('>>'); 6123 $this->_out('/XObject <<'); 6124 $this->_putxobjectdict(); 6125 $this->_out('>>'); 6126 // visibility 6127 $this->_out('/Properties <</OC1 '.$this->n_ocg_print.' 0 R /OC2 '.$this->n_ocg_view.' 0 R>>'); 6128 // transparency 6129 $this->_out('/ExtGState <<'); 6130 foreach ($this->extgstates as $k => $extgstate) { 6131 $this->_out('/GS'.$k.' '.$extgstate['n'].' 0 R'); 6132 } 6133 $this->_out('>>'); 6134 // gradients 6135 if (isset($this->gradients) AND (count($this->gradients) > 0)) { 6136 $this->_out('/Shading <<'); 6137 foreach ($this->gradients as $id => $grad) { 6138 $this->_out('/Sh'.$id.' '.$grad['id'].' 0 R'); 6139 } 6140 $this->_out('>>'); 6141 } 6142 // spot colors 6143 if (isset($this->spot_colors) AND (count($this->spot_colors) > 0)) { 6144 $this->_out('/ColorSpace <<'); 6145 foreach ($this->spot_colors as $color) { 6146 $this->_out('/CS'.$color['i'].' '.$color['n'].' 0 R'); 6147 } 6148 $this->_out('>>'); 6149 } 6150 } 6151 6152 /** 6153 * Output Resources. 6154 * @access protected 6155 */ 6156 protected function _putresources() { 6157 $this->_putextgstates(); 6158 $this->_putocg(); 6159 $this->_putfonts(); 6160 $this->_putimages(); 6161 $this->_putspotcolors(); 6162 $this->_putshaders(); 6163 //Resource dictionary 6164 $this->offsets[2] = $this->bufferlen; 6165 $this->_out('2 0 obj'); 6166 $this->_out('<<'); 6167 $this->_putresourcedict(); 6168 $this->_out('>>'); 6169 $this->_out('endobj'); 6170 $this->_putjavascript(); 6171 $this->_putbookmarks(); 6172 $this->_putEmbeddedFiles(); 6173 // encryption 6174 if ($this->encrypted) { 6175 $this->_newobj(); 6176 $this->enc_obj_id = $this->n; 6177 $this->_out('<<'); 6178 $this->_putencryption(); 6179 $this->_out('>>'); 6180 $this->_out('endobj'); 6181 } 6182 } 6183 6184 /** 6185 * Adds some Metadata information 6186 * (see Chapter 10.2 of PDF Reference) 6187 * @access protected 6188 */ 6189 protected function _putinfo() { 6190 if (!$this->empty_string($this->title)) { 6191 $this->_out('/Title '.$this->_textstring($this->title)); 6192 } 6193 if (!$this->empty_string($this->author)) { 6194 $this->_out('/Author '.$this->_textstring($this->author)); 6195 } 6196 if (!$this->empty_string($this->subject)) { 6197 $this->_out('/Subject '.$this->_textstring($this->subject)); 6198 } 6199 if (!$this->empty_string($this->keywords)) { 6200 $this->_out('/Keywords '.$this->_textstring($this->keywords)); 6201 } 6202 if (!$this->empty_string($this->creator)) { 6203 $this->_out('/Creator '.$this->_textstring($this->creator)); 6204 } 6205 if (defined('PDF_PRODUCER')) { 6206 $this->_out('/Producer '.$this->_textstring(PDF_PRODUCER)); 6207 } 6208 $this->_out('/CreationDate '.$this->_datastring('D:'.date('YmdHis'))); 6209 $this->_out('/ModDate '.$this->_datastring('D:'.date('YmdHis'))); 6210 } 6211 6212 /** 6213 * Output Catalog. 6214 * @access protected 6215 */ 6216 protected function _putcatalog() { 6217 $this->_out('/Type /Catalog'); 6218 $this->_out('/Pages 1 0 R'); 6219 if ($this->ZoomMode == 'fullpage') { 6220 $this->_out('/OpenAction [3 0 R /Fit]'); 6221 } elseif ($this->ZoomMode == 'fullwidth') { 6222 $this->_out('/OpenAction [3 0 R /FitH null]'); 6223 } elseif ($this->ZoomMode == 'real') { 6224 $this->_out('/OpenAction [3 0 R /XYZ null null 1]'); 6225 } elseif (!is_string($this->ZoomMode)) { 6226 $this->_out('/OpenAction [3 0 R /XYZ null null '.($this->ZoomMode / 100).']'); 6227 } 6228 if (isset($this->LayoutMode) AND (!$this->empty_string($this->LayoutMode))) { 6229 $this->_out('/PageLayout /'.$this->LayoutMode.''); 6230 } 6231 if (isset($this->PageMode) AND (!$this->empty_string($this->PageMode))) { 6232 $this->_out('/PageMode /'.$this->PageMode); 6233 } 6234 if (isset($this->l['a_meta_language'])) { 6235 $this->_out('/Lang /'.$this->l['a_meta_language']); 6236 } 6237 $this->_out('/Names <<'); 6238 if (!$this->empty_string($this->javascript)) { 6239 $this->_out('/JavaScript '.($this->n_js).' 0 R'); 6240 } 6241 $this->_out('>>'); 6242 if (count($this->outlines) > 0) { 6243 $this->_out('/Outlines '.$this->OutlineRoot.' 0 R'); 6244 $this->_out('/PageMode /UseOutlines'); 6245 } 6246 $this->_putviewerpreferences(); 6247 $p = $this->n_ocg_print.' 0 R'; 6248 $v = $this->n_ocg_view.' 0 R'; 6249 $as = '<</Event /Print /OCGs ['.$p.' '.$v.'] /Category [/Print]>> <</Event /View /OCGs ['.$p.' '.$v.'] /Category [/View]>>'; 6250 $this->_out('/OCProperties <</OCGs ['.$p.' '.$v.'] /D <</ON ['.$p.'] /OFF ['.$v.'] /AS ['.$as.']>>>>'); 6251 } 6252 6253 /** 6254 * Output viewer preferences. 6255 * @author Nicola asuni 6256 * @since 3.1.000 (2008-06-09) 6257 * @access protected 6258 */ 6259 protected function _putviewerpreferences() { 6260 $this->_out('/ViewerPreferences<<'); 6261 if ($this->rtl) { 6262 $this->_out('/Direction /R2L'); 6263 } else { 6264 $this->_out('/Direction /L2R'); 6265 } 6266 if (isset($this->viewer_preferences['HideToolbar']) AND ($this->viewer_preferences['HideToolbar'])) { 6267 $this->_out('/HideToolbar true'); 6268 } 6269 if (isset($this->viewer_preferences['HideMenubar']) AND ($this->viewer_preferences['HideMenubar'])) { 6270 $this->_out('/HideMenubar true'); 6271 } 6272 if (isset($this->viewer_preferences['HideWindowUI']) AND ($this->viewer_preferences['HideWindowUI'])) { 6273 $this->_out('/HideWindowUI true'); 6274 } 6275 if (isset($this->viewer_preferences['FitWindow']) AND ($this->viewer_preferences['FitWindow'])) { 6276 $this->_out('/FitWindow true'); 6277 } 6278 if (isset($this->viewer_preferences['CenterWindow']) AND ($this->viewer_preferences['CenterWindow'])) { 6279 $this->_out('/CenterWindow true'); 6280 } 6281 if (isset($this->viewer_preferences['DisplayDocTitle']) AND ($this->viewer_preferences['DisplayDocTitle'])) { 6282 $this->_out('/DisplayDocTitle true'); 6283 } 6284 if (isset($this->viewer_preferences['NonFullScreenPageMode'])) { 6285 $this->_out('/NonFullScreenPageMode /'.$this->viewer_preferences['NonFullScreenPageMode'].''); 6286 } 6287 if (isset($this->viewer_preferences['ViewArea'])) { 6288 $this->_out('/ViewArea /'.$this->viewer_preferences['ViewArea']); 6289 } 6290 if (isset($this->viewer_preferences['ViewClip'])) { 6291 $this->_out('/ViewClip /'.$this->viewer_preferences['ViewClip']); 6292 } 6293 if (isset($this->viewer_preferences['PrintArea'])) { 6294 $this->_out('/PrintArea /'.$this->viewer_preferences['PrintArea']); 6295 } 6296 if (isset($this->viewer_preferences['PrintClip'])) { 6297 $this->_out('/PrintClip /'.$this->viewer_preferences['PrintClip']); 6298 } 6299 if (isset($this->viewer_preferences['PrintScaling'])) { 6300 $this->_out('/PrintScaling /'.$this->viewer_preferences['PrintScaling']); 6301 } 6302 if (isset($this->viewer_preferences['Duplex']) AND (!$this->empty_string($this->viewer_preferences['Duplex']))) { 6303 $this->_out('/Duplex /'.$this->viewer_preferences['Duplex']); 6304 } 6305 if (isset($this->viewer_preferences['PickTrayByPDFSize'])) { 6306 if ($this->viewer_preferences['PickTrayByPDFSize']) { 6307 $this->_out('/PickTrayByPDFSize true'); 6308 } else { 6309 $this->_out('/PickTrayByPDFSize false'); 6310 } 6311 } 6312 if (isset($this->viewer_preferences['PrintPageRange'])) { 6313 $PrintPageRangeNum = ''; 6314 foreach ($this->viewer_preferences['PrintPageRange'] as $k => $v) { 6315 $PrintPageRangeNum .= ' '.($v - 1).''; 6316 } 6317 $this->_out('/PrintPageRange ['.substr($PrintPageRangeNum,1).']'); 6318 } 6319 if (isset($this->viewer_preferences['NumCopies'])) { 6320 $this->_out('/NumCopies '.intval($this->viewer_preferences['NumCopies'])); 6321 } 6322 $this->_out('>>'); 6323 } 6324 6325 /** 6326 * Output trailer. 6327 * @access protected 6328 */ 6329 protected function _puttrailer() { 6330 $this->_out('/Size '.($this->n + 1)); 6331 $this->_out('/Root '.$this->n.' 0 R'); 6332 $this->_out('/Info '.($this->n - 1).' 0 R'); 6333 if ($this->encrypted) { 6334 $this->_out('/Encrypt '.$this->enc_obj_id.' 0 R'); 6335 $this->_out('/ID [()()]'); 6336 } 6337 } 6338 6339 /** 6340 * Output PDF header. 6341 * @access protected 6342 */ 6343 protected function _putheader() { 6344 $this->_out('%PDF-'.$this->PDFVersion); 6345 } 6346 6347 /** 6348 * Output end of document (EOF). 6349 * @access protected 6350 */ 6351 protected function _enddoc() { 6352 $this->state = 1; 6353 $this->_putheader(); 6354 $this->_putpages(); 6355 $this->_putresources(); 6356 //Info 6357 $this->_newobj(); 6358 $this->_out('<<'); 6359 $this->_putinfo(); 6360 $this->_out('>>'); 6361 $this->_out('endobj'); 6362 //Catalog 6363 $this->_newobj(); 6364 $this->_out('<<'); 6365 $this->_putcatalog(); 6366 $this->_putcertification(); 6367 $this->_putuserrights(); 6368 $this->_out('>>'); 6369 $this->_out('endobj'); 6370 //Cross-ref 6371 $o = $this->bufferlen; 6372 $this->_out('xref'); 6373 $this->_out('0 '.($this->n + 1)); 6374 $this->_out('0000000000 65535 f '); 6375 for ($i=1; $i <= $this->n; ++$i) { 6376 $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i])); 6377 } 6378 if (isset($this->embeddedfiles) AND count($this->embeddedfiles) > 0) { 6379 $this->_out('100000 '.count($this->embeddedfiles)); 6380 foreach ($this->embeddedfiles as $filename => $filedata) { 6381 $this->_out(sprintf('%010d 00000 n ', $this->offsets[$filedata['n']])); 6382 } 6383 } 6384 //Trailer 6385 $this->_out('trailer'); 6386 $this->_out('<<'); 6387 $this->_puttrailer(); 6388 $this->_out('>>'); 6389 $this->_out('startxref'); 6390 $this->_out($o); 6391 $this->_out('%%EOF'); 6392 $this->state = 3; // end-of-doc 6393 if ($this->diskcache) { 6394 // remove temporary files used for images 6395 foreach ($this->imagekeys as $key) { 6396 // remove temporary files 6397 unlink($this->images[$key]); 6398 } 6399 foreach ($this->fontkeys as $key) { 6400 // remove temporary files 6401 unlink($this->fonts[$key]); 6402 } 6403 } 6404 } 6405 6406 /** 6407 * Initialize a new page. 6408 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul> 6409 * @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> 6410 * @access protected 6411 */ 6412 protected function _beginpage($orientation='', $format='') { 6413 ++$this->page; 6414 $this->setPageBuffer($this->page, ''); 6415 // initialize array for graphics tranformation positions inside a page buffer 6416 $this->transfmrk[$this->page] = array(); 6417 $this->state = 2; 6418 if ($this->empty_string($orientation)) { 6419 if (isset($this->CurOrientation)) { 6420 $orientation = $this->CurOrientation; 6421 } else { 6422 $orientation = 'P'; 6423 } 6424 } 6425 if ($this->empty_string($format)) { 6426 $this->setPageOrientation($orientation); 6427 } else { 6428 $this->setPageFormat($format, $orientation); 6429 } 6430 if ($this->rtl) { 6431 $this->x = $this->w - $this->rMargin; 6432 } else { 6433 $this->x = $this->lMargin; 6434 } 6435 $this->y = $this->tMargin; 6436 if (isset($this->newpagegroup[$this->page])) { 6437 // start a new group 6438 $n = sizeof($this->pagegroups) + 1; 6439 $alias = '{nb'.$n.'}'; 6440 $this->pagegroups[$alias] = 1; 6441 $this->currpagegroup = $alias; 6442 } elseif ($this->currpagegroup) { 6443 ++$this->pagegroups[$this->currpagegroup]; 6444 } 6445 } 6446 6447 /** 6448 * Mark end of page. 6449 * @access protected 6450 */ 6451 protected function _endpage() { 6452 $this->setVisibility('all'); 6453 $this->state = 1; 6454 } 6455 6456 /** 6457 * Begin a new object. 6458 * @access protected 6459 */ 6460 protected function _newobj() { 6461 ++$this->n; 6462 $this->offsets[$this->n] = $this->bufferlen; 6463 $this->_out($this->n.' 0 obj'); 6464 } 6465 6466 /** 6467 * Underline text. 6468 * @param int $x X coordinate 6469 * @param int $y Y coordinate 6470 * @param string $txt text to underline 6471 * @access protected 6472 */ 6473 protected function _dounderline($x, $y, $txt) { 6474 $up = $this->CurrentFont['up']; 6475 $ut = $this->CurrentFont['ut']; 6476 $w = $this->GetStringWidth($txt); 6477 return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, ($this->h - ($y - $up / 1000 * $this->FontSize)) * $this->k, $w * $this->k, -$ut / 1000 * $this->FontSizePt); 6478 } 6479 6480 /** 6481 * Line through text. 6482 * @param int $x X coordinate 6483 * @param int $y Y coordinate 6484 * @param string $txt text to linethrough 6485 * @access protected 6486 */ 6487 protected function _dolinethrough($x, $y, $txt) { 6488 $up = $this->CurrentFont['up']; 6489 $ut = $this->CurrentFont['ut']; 6490 $w = $this->GetStringWidth($txt); 6491 return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, ($this->h - ($y - ($this->FontSize/2) - $up / 1000 * $this->FontSize)) * $this->k, $w * $this->k, -$ut / 1000 * $this->FontSizePt); 6492 } 6493 6494 /** 6495 * Read a 4-byte integer from file. 6496 * @param string $f file name. 6497 * @return 4-byte integer 6498 * @access protected 6499 */ 6500 protected function _freadint($f) { 6501 $a = unpack('Ni', fread($f, 4)); 6502 return $a['i']; 6503 } 6504 6505 /** 6506 * Add "\" before "\", "(" and ")" 6507 * @param string $s string to escape. 6508 * @return string escaped string. 6509 * @access protected 6510 */ 6511 protected function _escape($s) { 6512 // the chr(13) substitution fixes the Bugs item #1421290. 6513 return strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r')); 6514 } 6515 6516 /** 6517 * Format a date string for meta information 6518 * @param string $s date string to escape. 6519 * @return string escaped string. 6520 * @access protected 6521 */ 6522 protected function _datastring($s) { 6523 if ($this->encrypted) { 6524 $s = $this->_RC4($this->_objectkey($this->n), $s); 6525 } 6526 return '('. $this->_escape($s).')'; 6527 } 6528 6529 /** 6530 * Format a text string for meta information 6531 * @param string $s string to escape. 6532 * @return string escaped string. 6533 * @access protected 6534 */ 6535 protected function _textstring($s) { 6536 if ($this->isunicode) { 6537 //Convert string to UTF-16BE 6538 $s = $this->UTF8ToUTF16BE($s, true); 6539 } 6540 return $this->_datastring($s); 6541 } 6542 6543 /** 6544 * Format a text string 6545 * @param string $s string to escape. 6546 * @return string escaped string. 6547 * @access protected 6548 */ 6549 protected function _escapetext($s) { 6550 if ($this->isunicode) { 6551 if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) { 6552 $s = $this->UTF8ToLatin1($s); 6553 } else { 6554 //Convert string to UTF-16BE and reverse RTL language 6555 $s = $this->utf8StrRev($s, false, $this->tmprtl); 6556 } 6557 } 6558 return $this->_escape($s); 6559 } 6560 6561 /** 6562 * Output a stream. 6563 * @param string $s string to output. 6564 * @access protected 6565 */ 6566 protected function _putstream($s) { 6567 if ($this->encrypted) { 6568 $s = $this->_RC4($this->_objectkey($this->n), $s); 6569 } 6570 $this->_out('stream'); 6571 $this->_out($s); 6572 $this->_out('endstream'); 6573 } 6574 6575 /** 6576 * Output a string to the document. 6577 * @param string $s string to output. 6578 * @access protected 6579 */ 6580 protected function _out($s) { 6581 if ($this->state == 2) { 6582 if ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) { 6583 // puts data before page footer 6584 $pagebuff = $this->getPageBuffer($this->page); 6585 $page = substr($pagebuff, 0, -$this->footerlen[$this->page]); 6586 $footer = substr($pagebuff, -$this->footerlen[$this->page]); 6587 $this->setPageBuffer($this->page, $page.$s."\n".$footer); 6588 // update footer position 6589 $this->footerpos[$this->page] += strlen($s."\n"); 6590 } else { 6591 $this->setPageBuffer($this->page, $s."\n", true); 6592 } 6593 } else { 6594 $this->setBuffer($s."\n"); 6595 } 6596 } 6597 6598 /** 6599 * Converts UTF-8 strings to codepoints array.<br> 6600 * Invalid byte sequences will be replaced with 0xFFFD (replacement character)<br> 6601 * Based on: http://www.faqs.org/rfcs/rfc3629.html 6602 * <pre> 6603 * Char. number range | UTF-8 octet sequence 6604 * (hexadecimal) | (binary) 6605 * --------------------+----------------------------------------------- 6606 * 0000 0000-0000 007F | 0xxxxxxx 6607 * 0000 0080-0000 07FF | 110xxxxx 10xxxxxx 6608 * 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx 6609 * 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 6610 * --------------------------------------------------------------------- 6611 * 6612 * ABFN notation: 6613 * --------------------------------------------------------------------- 6614 * UTF8-octets = *( UTF8-char ) 6615 * UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4 6616 * UTF8-1 = %x00-7F 6617 * UTF8-2 = %xC2-DF UTF8-tail 6618 * 6619 * UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) / 6620 * %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail ) 6621 * UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) / 6622 * %xF4 %x80-8F 2( UTF8-tail ) 6623 * UTF8-tail = %x80-BF 6624 * --------------------------------------------------------------------- 6625 * </pre> 6626 * @param string $str string to process. 6627 * @return array containing codepoints (UTF-8 characters values) 6628 * @access protected 6629 * @author Nicola Asuni 6630 * @since 1.53.0.TC005 (2005-01-05) 6631 */ 6632 protected function UTF8StringToArray($str) { 6633 if (isset($this->cache_UTF8StringToArray['_'.$str])) { 6634 // return cached value 6635 return($this->cache_UTF8StringToArray['_'.$str]); 6636 } 6637 // check cache size 6638 if ($this->cache_size_UTF8StringToArray >= $this->cache_maxsize_UTF8StringToArray) { 6639 // remove first element 6640 array_shift($this->cache_UTF8StringToArray); 6641 } 6642 ++$this->cache_size_UTF8StringToArray; 6643 if (!$this->isunicode) { 6644 // split string into array of equivalent codes 6645 $strarr = array(); 6646 $strlen = strlen($str); 6647 for ($i=0; $i < $strlen; ++$i) { 6648 $strarr[] = ord($str{$i}); 6649 } 6650 // insert new value on cache 6651 $this->cache_UTF8StringToArray['_'.$str] = $strarr; 6652 return $strarr; 6653 } 6654 $unicode = array(); // array containing unicode values 6655 $bytes = array(); // array containing single character byte sequences 6656 $numbytes = 1; // number of octetc needed to represent the UTF-8 character 6657 $str .= ''; // force $str to be a string 6658 $length = strlen($str); 6659 for ($i = 0; $i < $length; ++$i) { 6660 $char = ord($str{$i}); // get one string character at time 6661 if (count($bytes) == 0) { // get starting octect 6662 if ($char <= 0x7F) { 6663 $unicode[] = $char; // use the character "as is" because is ASCII 6664 $numbytes = 1; 6665 } elseif (($char >> 0x05) == 0x06) { // 2 bytes character (0x06 = 110 BIN) 6666 $bytes[] = ($char - 0xC0) << 0x06; 6667 $numbytes = 2; 6668 } elseif (($char >> 0x04) == 0x0E) { // 3 bytes character (0x0E = 1110 BIN) 6669 $bytes[] = ($char - 0xE0) << 0x0C; 6670 $numbytes = 3; 6671 } elseif (($char >> 0x03) == 0x1E) { // 4 bytes character (0x1E = 11110 BIN) 6672 $bytes[] = ($char - 0xF0) << 0x12; 6673 $numbytes = 4; 6674 } else { 6675 // use replacement character for other invalid sequences 6676 $unicode[] = 0xFFFD; 6677 $bytes = array(); 6678 $numbytes = 1; 6679 } 6680 } elseif (($char >> 0x06) == 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN 6681 $bytes[] = $char - 0x80; 6682 if (count($bytes) == $numbytes) { 6683 // compose UTF-8 bytes to a single unicode value 6684 $char = $bytes[0]; 6685 for ($j = 1; $j < $numbytes; ++$j) { 6686 $char += ($bytes[$j] << (($numbytes - $j - 1) * 0x06)); 6687 } 6688 if ((($char >= 0xD800) AND ($char <= 0xDFFF)) OR ($char >= 0x10FFFF)) { 6689 /* The definition of UTF-8 prohibits encoding character numbers between 6690 U+D800 and U+DFFF, which are reserved for use with the UTF-16 6691 encoding form (as surrogate pairs) and do not directly represent 6692 characters. */ 6693 $unicode[] = 0xFFFD; // use replacement character 6694 } else { 6695 $unicode[] = $char; // add char to array 6696 } 6697 // reset data for next char 6698 $bytes = array(); 6699 $numbytes = 1; 6700 } 6701 } else { 6702 // use replacement character for other invalid sequences 6703 $unicode[] = 0xFFFD; 6704 $bytes = array(); 6705 $numbytes = 1; 6706 } 6707 } 6708 // insert new value on cache 6709 $this->cache_UTF8StringToArray['_'.$str] = $unicode; 6710 return $unicode; 6711 } 6712 6713 /** 6714 * Converts UTF-8 strings to UTF16-BE.<br> 6715 * @param string $str string to process. 6716 * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF) 6717 * @return string 6718 * @access protected 6719 * @author Nicola Asuni 6720 * @since 1.53.0.TC005 (2005-01-05) 6721 * @uses UTF8StringToArray(), arrUTF8ToUTF16BE() 6722 */ 6723 protected function UTF8ToUTF16BE($str, $setbom=true) { 6724 if (!$this->isunicode) { 6725 return $str; // string is not in unicode 6726 } 6727 $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values 6728 return $this->arrUTF8ToUTF16BE($unicode, $setbom); 6729 } 6730 6731 /** 6732 * Converts UTF-8 strings to Latin1 when using the standard 14 core fonts.<br> 6733 * @param string $str string to process. 6734 * @return string 6735 * @author Andrew Whitehead, Nicola Asuni 6736 * @access protected 6737 * @since 3.2.000 (2008-06-23) 6738 */ 6739 protected function UTF8ToLatin1($str) { 6740 global $utf8tolatin; 6741 if (!$this->isunicode) { 6742 return $str; // string is not in unicode 6743 } 6744 $outstr = ''; // string to be returned 6745 $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values 6746 foreach ($unicode as $char) { 6747 if ($char < 256) { 6748 $outstr .= chr($char); 6749 } elseif (array_key_exists($char, $utf8tolatin)) { 6750 // map from UTF-8 6751 $outstr .= chr($utf8tolatin[$char]); 6752 } elseif ($char == 0xFFFD) { 6753 // skip 6754 } else { 6755 $outstr .= '?'; 6756 } 6757 } 6758 return $outstr; 6759 } 6760 6761 /** 6762 * Converts array of UTF-8 characters to UTF16-BE string.<br> 6763 * Based on: http://www.faqs.org/rfcs/rfc2781.html 6764 * <pre> 6765 * Encoding UTF-16: 6766 * 6767 * Encoding of a single character from an ISO 10646 character value to 6768 * UTF-16 proceeds as follows. Let U be the character number, no greater 6769 * than 0x10FFFF. 6770 * 6771 * 1) If U < 0x10000, encode U as a 16-bit unsigned integer and 6772 * terminate. 6773 * 6774 * 2) Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF, 6775 * U' must be less than or equal to 0xFFFFF. That is, U' can be 6776 * represented in 20 bits. 6777 * 6778 * 3) Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and 6779 * 0xDC00, respectively. These integers each have 10 bits free to 6780 * encode the character value, for a total of 20 bits. 6781 * 6782 * 4) Assign the 10 high-order bits of the 20-bit U' to the 10 low-order 6783 * bits of W1 and the 10 low-order bits of U' to the 10 low-order 6784 * bits of W2. Terminate. 6785 * 6786 * Graphically, steps 2 through 4 look like: 6787 * U' = yyyyyyyyyyxxxxxxxxxx 6788 * W1 = 110110yyyyyyyyyy 6789 * W2 = 110111xxxxxxxxxx 6790 * </pre> 6791 * @param array $unicode array containing UTF-8 unicode values 6792 * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF) 6793 * @return string 6794 * @access protected 6795 * @author Nicola Asuni 6796 * @since 2.1.000 (2008-01-08) 6797 * @see UTF8ToUTF16BE() 6798 */ 6799 protected function arrUTF8ToUTF16BE($unicode, $setbom=true) { 6800 $outstr = ''; // string to be returned 6801 if ($setbom) { 6802 $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM) 6803 } 6804 foreach ($unicode as $char) { 6805 if ($char == 0xFFFD) { 6806 $outstr .= "\xFF\xFD"; // replacement character 6807 } elseif ($char < 0x10000) { 6808 $outstr .= chr($char >> 0x08); 6809 $outstr .= chr($char & 0xFF); 6810 } else { 6811 $char -= 0x10000; 6812 $w1 = 0xD800 | ($char >> 0x10); 6813 $w2 = 0xDC00 | ($char & 0x3FF); 6814 $outstr .= chr($w1 >> 0x08); 6815 $outstr .= chr($w1 & 0xFF); 6816 $outstr .= chr($w2 >> 0x08); 6817 $outstr .= chr($w2 & 0xFF); 6818 } 6819 } 6820 return $outstr; 6821 } 6822 // ==================================================== 6823 6824 /** 6825 * Set header font. 6826 * @param array $font font 6827 * @access public 6828 * @since 1.1 6829 */ 6830 public function setHeaderFont($font) { 6831 $this->header_font = $font; 6832 } 6833 6834 /** 6835 * Get header font. 6836 * @return array() 6837 * @access public 6838 * @since 4.0.012 (2008-07-24) 6839 */ 6840 public function getHeaderFont() { 6841 return $this->header_font; 6842 } 6843 6844 /** 6845 * Set footer font. 6846 * @param array $font font 6847 * @access public 6848 * @since 1.1 6849 */ 6850 public function setFooterFont($font) { 6851 $this->footer_font = $font; 6852 } 6853 6854 /** 6855 * Get Footer font. 6856 * @return array() 6857 * @access public 6858 * @since 4.0.012 (2008-07-24) 6859 */ 6860 public function getFooterFont() { 6861 return $this->footer_font; 6862 } 6863 6864 /** 6865 * Set language array. 6866 * @param array $language 6867 * @access public 6868 * @since 1.1 6869 */ 6870 public function setLanguageArray($language) { 6871 $this->l = $language; 6872 $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false; 6873 } 6874 6875 /** 6876 * Returns the PDF data. 6877 * @access public 6878 */ 6879 public function getPDFData() { 6880 if ($this->state < 3) { 6881 $this->Close(); 6882 } 6883 return $this->buffer; 6884 } 6885 6886 /** 6887 * Output anchor link. 6888 * @param string $url link URL or internal link (i.e.: <a href="#23">link to page 23</a>) 6889 * @param string $name link name 6890 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. 6891 * @param boolean $firstline if true prints only the first line and return the remaining string. 6892 * @param array $color array of RGB text color 6893 * @param string $style font style (U, D, B, I) 6894 * @return the number of cells used or the remaining text if $firstline = true; 6895 * @access public 6896 */ 6897 public function addHtmlLink($url, $name, $fill=0, $firstline=false, $color='', $style=-1) { 6898 if (!$this->empty_string($url) AND ($url{0} == '#')) { 6899 // convert url to internal link 6900 $page = intval(substr($url, 1)); 6901 $url = $this->AddLink(); 6902 $this->SetLink($url, 0, $page); 6903 } 6904 // store current settings 6905 $prevcolor = $this->fgcolor; 6906 $prevstyle = $this->FontStyle; 6907 if (empty($color)) { 6908 $this->SetTextColorArray($this->htmlLinkColorArray); 6909 } else { 6910 $this->SetTextColorArray($color); 6911 } 6912 if ($style == -1) { 6913 $this->SetFont('', $this->FontStyle.$this->htmlLinkFontStyle); 6914 } else { 6915 $this->SetFont('', $this->FontStyle.$style); 6916 } 6917 $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline); 6918 // restore settings 6919 $this->SetFont('', $prevstyle); 6920 $this->SetTextColorArray($prevcolor); 6921 return $ret; 6922 } 6923 6924 /** 6925 * 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). 6926 * @param string $color html color 6927 * @return array RGB color or false in case of error. 6928 * @access public 6929 */ 6930 public function convertHTMLColorToDec($color='#FFFFFF') { 6931 global $webcolor; 6932 $returncolor = false; 6933 $color = preg_replace('/[\s]*/', '', $color); // remove extra spaces 6934 $color = strtolower($color); 6935 if (strlen($color) == 0) { 6936 return false; 6937 } 6938 if (substr($color, 0, 3) == 'rgb') { 6939 $codes = substr($color, 4); 6940 $codes = str_replace(')', '', $codes); 6941 $returncolor = explode(',', $codes, 3); 6942 return $returncolor; 6943 } 6944 if (substr($color, 0, 1) != '#') { 6945 // decode color name 6946 if (isset($webcolor[$color])) { 6947 $color_code = $webcolor[$color]; 6948 } else { 6949 return false; 6950 } 6951 } else { 6952 $color_code = substr($color, 1); 6953 } 6954 switch (strlen($color_code)) { 6955 case 3: { 6956 // three-digit hexadecimal representation 6957 $r = substr($color_code, 0, 1); 6958 $g = substr($color_code, 1, 1); 6959 $b = substr($color_code, 2, 1); 6960 $returncolor['R'] = hexdec($r.$r); 6961 $returncolor['G'] = hexdec($g.$g); 6962 $returncolor['B'] = hexdec($b.$b); 6963 break; 6964 } 6965 case 6: { 6966 // six-digit hexadecimal representation 6967 $returncolor['R'] = hexdec(substr($color_code, 0, 2)); 6968 $returncolor['G'] = hexdec(substr($color_code, 2, 2)); 6969 $returncolor['B'] = hexdec(substr($color_code, 4, 2)); 6970 break; 6971 } 6972 } 6973 return $returncolor; 6974 } 6975 6976 /** 6977 * Converts pixels to User's Units. 6978 * @param int $px pixels 6979 * @return float value in user's unit 6980 * @access public 6981 * @see setImageScale(), getImageScale() 6982 */ 6983 public function pixelsToUnits($px) { 6984 return ($px / ($this->imgscale * $this->k)); 6985 } 6986 6987 /** 6988 * Reverse function for htmlentities. 6989 * Convert entities in UTF-8. 6990 * @param $text_to_convert Text to convert. 6991 * @return string converted 6992 * @access public 6993 */ 6994 public function unhtmlentities($text_to_convert) { 6995 return html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding); 6996 } 6997 6998 // ENCRYPTION METHODS ---------------------------------- 6999 // SINCE 2.0.000 (2008-01-02) 7000 7001 /** 7002 * Compute encryption key depending on object number where the encrypted data is stored 7003 * @param int $n object number 7004 * @access protected 7005 * @since 2.0.000 (2008-01-02) 7006 */ 7007 protected function _objectkey($n) { 7008 return substr($this->_md5_16($this->encryption_key.pack('VXxx', $n)), 0, 10); 7009 } 7010 7011 /** 7012 * Put encryption on PDF document. 7013 * @access protected 7014 * @since 2.0.000 (2008-01-02) 7015 */ 7016 protected function _putencryption() { 7017 $this->_out('/Filter /Standard'); 7018 $this->_out('/V 1'); 7019 $this->_out('/R 2'); 7020 $this->_out('/O ('.$this->_escape($this->Ovalue).')'); 7021 $this->_out('/U ('.$this->_escape($this->Uvalue).')'); 7022 $this->_out('/P '.$this->Pvalue); 7023 } 7024 7025 /** 7026 * Returns the input text exrypted using RC4 algorithm and the specified key. 7027 * RC4 is the standard encryption algorithm used in PDF format 7028 * @param string $key encryption key 7029 * @param String $text input text to be encrypted 7030 * @return String encrypted text 7031 * @access protected 7032 * @since 2.0.000 (2008-01-02) 7033 * @author Klemen Vodopivec 7034 */ 7035 protected function _RC4($key, $text) { 7036 if ($this->last_rc4_key != $key) { 7037 $k = str_repeat($key, ((256 / strlen($key)) + 1)); 7038 $rc4 = range(0, 255); 7039 $j = 0; 7040 for ($i = 0; $i < 256; ++$i) { 7041 $t = $rc4[$i]; 7042 $j = ($j + $t + ord($k{$i})) % 256; 7043 $rc4[$i] = $rc4[$j]; 7044 $rc4[$j] = $t; 7045 } 7046 $this->last_rc4_key = $key; 7047 $this->last_rc4_key_c = $rc4; 7048 } else { 7049 $rc4 = $this->last_rc4_key_c; 7050 } 7051 $len = strlen($text); 7052 $a = 0; 7053 $b = 0; 7054 $out = ''; 7055 for ($i = 0; $i < $len; ++$i) { 7056 $a = ($a + 1) % 256; 7057 $t = $rc4[$a]; 7058 $b = ($b + $t) % 256; 7059 $rc4[$a] = $rc4[$b]; 7060 $rc4[$b] = $t; 7061 $k = $rc4[($rc4[$a] + $rc4[$b]) % 256]; 7062 $out .= chr(ord($text{$i}) ^ $k); 7063 } 7064 return $out; 7065 } 7066 7067 /** 7068 * Encrypts a string using MD5 and returns it's value as a binary string. 7069 * @param string $str input string 7070 * @return String MD5 encrypted binary string 7071 * @access protected 7072 * @since 2.0.000 (2008-01-02) 7073 * @author Klemen Vodopivec 7074 */ 7075 protected function _md5_16($str) { 7076 return pack('H*', md5($str)); 7077 } 7078 7079 /** 7080 * Compute O value (used for RC4 encryption) 7081 * @param String $user_pass user password 7082 * @param String $owner_pass user password 7083 * @return String O value 7084 * @access protected 7085 * @since 2.0.000 (2008-01-02) 7086 * @author Klemen Vodopivec 7087 */ 7088 protected function _Ovalue($user_pass, $owner_pass) { 7089 $tmp = $this->_md5_16($owner_pass); 7090 $owner_RC4_key = substr($tmp, 0, 5); 7091 return $this->_RC4($owner_RC4_key, $user_pass); 7092 } 7093 7094 /** 7095 * Compute U value (used for RC4 encryption) 7096 * @return String U value 7097 * @access protected 7098 * @since 2.0.000 (2008-01-02) 7099 * @author Klemen Vodopivec 7100 */ 7101 protected function _Uvalue() { 7102 return $this->_RC4($this->encryption_key, $this->padding); 7103 } 7104 7105 /** 7106 * Compute encryption key 7107 * @param String $user_pass user password 7108 * @param String $owner_pass user password 7109 * @param String $protection protection type 7110 * @access protected 7111 * @since 2.0.000 (2008-01-02) 7112 * @author Klemen Vodopivec 7113 */ 7114 protected function _generateencryptionkey($user_pass, $owner_pass, $protection) { 7115 // Pad passwords 7116 $user_pass = substr($user_pass.$this->padding, 0, 32); 7117 $owner_pass = substr($owner_pass.$this->padding, 0, 32); 7118 // Compute O value 7119 $this->Ovalue = $this->_Ovalue($user_pass, $owner_pass); 7120 // Compute encyption key 7121 $tmp = $this->_md5_16($user_pass.$this->Ovalue.chr($protection)."\xFF\xFF\xFF"); 7122 $this->encryption_key = substr($tmp, 0, 5); 7123 // Compute U value 7124 $this->Uvalue = $this->_Uvalue(); 7125 // Compute P value 7126 $this->Pvalue = -(($protection^255) + 1); 7127 } 7128 7129 /** 7130 * Set document protection 7131 * The permission array is composed of values taken from the following ones: 7132 * - copy: copy text and images to the clipboard 7133 * - print: print the document 7134 * - modify: modify it (except for annotations and forms) 7135 * - annot-forms: add annotations and forms 7136 * Remark: the protection against modification is for people who have the full Acrobat product. 7137 * 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. 7138 * 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. 7139 * @param Array $permissions the set of permissions. Empty by default (only viewing is allowed). (print, modify, copy, annot-forms) 7140 * @param String $user_pass user password. Empty by default. 7141 * @param String $owner_pass owner password. If not specified, a random value is used. 7142 * @access public 7143 * @since 2.0.000 (2008-01-02) 7144 * @author Klemen Vodopivec 7145 */ 7146 public function SetProtection($permissions=array(), $user_pass='', $owner_pass=null) { 7147 $options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'annot-forms' => 32); 7148 $protection = 192; 7149 foreach ($permissions as $permission) { 7150 if (!isset($options[$permission])) { 7151 $this->Error('Incorrect permission: '.$permission); 7152 } 7153 $protection += $options[$permission]; 7154 } 7155 if ($owner_pass === null) { 7156 $owner_pass = uniqid(rand()); 7157 } 7158 $this->encrypted = true; 7159 $this->_generateencryptionkey($user_pass, $owner_pass, $protection); 7160 } 7161 7162 // END OF ENCRYPTION FUNCTIONS ------------------------- 7163 7164 // START TRANSFORMATIONS SECTION ----------------------- 7165 7166 /** 7167 * Starts a 2D tranformation saving current graphic state. 7168 * This function must be called before scaling, mirroring, translation, rotation and skewing. 7169 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior. 7170 * @access public 7171 * @since 2.1.000 (2008-01-07) 7172 * @see StartTransform(), StopTransform() 7173 */ 7174 public function StartTransform() { 7175 $this->_out('q'); 7176 $this->transfmrk[$this->page][] = $this->pagelen[$this->page]; 7177 } 7178 7179 /** 7180 * Stops a 2D tranformation restoring previous graphic state. 7181 * This function must be called after scaling, mirroring, translation, rotation and skewing. 7182 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior. 7183 * @access public 7184 * @since 2.1.000 (2008-01-07) 7185 * @see StartTransform(), StopTransform() 7186 */ 7187 public function StopTransform() { 7188 $this->_out('Q'); 7189 if (isset($this->transfmatrix)) { 7190 array_pop($this->transfmatrix); 7191 } 7192 array_pop($this->transfmrk[$this->page]); 7193 } 7194 /** 7195 * Horizontal Scaling. 7196 * @param float $s_x scaling factor for width as percent. 0 is not allowed. 7197 * @param int $x abscissa of the scaling center. Default is current x position 7198 * @param int $y ordinate of the scaling center. Default is current y position 7199 * @access public 7200 * @since 2.1.000 (2008-01-07) 7201 * @see StartTransform(), StopTransform() 7202 */ 7203 public function ScaleX($s_x, $x='', $y='') { 7204 $this->Scale($s_x, 100, $x, $y); 7205 } 7206 7207 /** 7208 * Vertical Scaling. 7209 * @param float $s_y scaling factor for height as percent. 0 is not allowed. 7210 * @param int $x abscissa of the scaling center. Default is current x position 7211 * @param int $y ordinate of the scaling center. Default is current y position 7212 * @access public 7213 * @since 2.1.000 (2008-01-07) 7214 * @see StartTransform(), StopTransform() 7215 */ 7216 public function ScaleY($s_y, $x='', $y='') { 7217 $this->Scale(100, $s_y, $x, $y); 7218 } 7219 7220 /** 7221 * Vertical and horizontal proportional Scaling. 7222 * @param float $s scaling factor for width and height as percent. 0 is not allowed. 7223 * @param int $x abscissa of the scaling center. Default is current x position 7224 * @param int $y ordinate of the scaling center. Default is current y position 7225 * @access public 7226 * @since 2.1.000 (2008-01-07) 7227 * @see StartTransform(), StopTransform() 7228 */ 7229 public function ScaleXY($s, $x='', $y='') { 7230 $this->Scale($s, $s, $x, $y); 7231 } 7232 7233 /** 7234 * Vertical and horizontal non-proportional Scaling. 7235 * @param float $s_x scaling factor for width as percent. 0 is not allowed. 7236 * @param float $s_y scaling factor for height as percent. 0 is not allowed. 7237 * @param int $x abscissa of the scaling center. Default is current x position 7238 * @param int $y ordinate of the scaling center. Default is current y position 7239 * @access public 7240 * @since 2.1.000 (2008-01-07) 7241 * @see StartTransform(), StopTransform() 7242 */ 7243 public function Scale($s_x, $s_y, $x='', $y='') { 7244 if ($x === '') { 7245 $x = $this->x; 7246 } 7247 if ($y === '') { 7248 $y = $this->y; 7249 } 7250 if ($this->rtl) { 7251 $x = $this->w - $x; 7252 } 7253 if (($s_x == 0) OR ($s_y == 0)) { 7254 $this->Error('Please do not use values equal to zero for scaling'); 7255 } 7256 $y = ($this->h - $y) * $this->k; 7257 $x *= $this->k; 7258 //calculate elements of transformation matrix 7259 $s_x /= 100; 7260 $s_y /= 100; 7261 $tm[0] = $s_x; 7262 $tm[1] = 0; 7263 $tm[2] = 0; 7264 $tm[3] = $s_y; 7265 $tm[4] = $x * (1 - $s_x); 7266 $tm[5] = $y * (1 - $s_y); 7267 //scale the coordinate system 7268 $this->Transform($tm); 7269 } 7270 7271 /** 7272 * Horizontal Mirroring. 7273 * @param int $x abscissa of the point. Default is current x position 7274 * @access public 7275 * @since 2.1.000 (2008-01-07) 7276 * @see StartTransform(), StopTransform() 7277 */ 7278 public function MirrorH($x='') { 7279 $this->Scale(-100, 100, $x); 7280 } 7281 7282 /** 7283 * Verical Mirroring. 7284 * @param int $y ordinate of the point. Default is current y position 7285 * @access public 7286 * @since 2.1.000 (2008-01-07) 7287 * @see StartTransform(), StopTransform() 7288 */ 7289 public function MirrorV($y='') { 7290 $this->Scale(100, -100, '', $y); 7291 } 7292 7293 /** 7294 * Point reflection mirroring. 7295 * @param int $x abscissa of the point. Default is current x position 7296 * @param int $y ordinate of the point. Default is current y position 7297 * @access public 7298 * @since 2.1.000 (2008-01-07) 7299 * @see StartTransform(), StopTransform() 7300 */ 7301 public function MirrorP($x='',$y='') { 7302 $this->Scale(-100, -100, $x, $y); 7303 } 7304 7305 /** 7306 * Reflection against a straight line through point (x, y) with the gradient angle (angle). 7307 * @param float $angle gradient angle of the straight line. Default is 0 (horizontal line). 7308 * @param int $x abscissa of the point. Default is current x position 7309 * @param int $y ordinate of the point. Default is current y position 7310 * @access public 7311 * @since 2.1.000 (2008-01-07) 7312 * @see StartTransform(), StopTransform() 7313 */ 7314 public function MirrorL($angle=0, $x='',$y='') { 7315 $this->Scale(-100, 100, $x, $y); 7316 $this->Rotate(-2*($angle-90), $x, $y); 7317 } 7318 7319 /** 7320 * Translate graphic object horizontally. 7321 * @param int $t_x movement to the right (or left for RTL) 7322 * @access public 7323 * @since 2.1.000 (2008-01-07) 7324 * @see StartTransform(), StopTransform() 7325 */ 7326 public function TranslateX($t_x) { 7327 $this->Translate($t_x, 0); 7328 } 7329 7330 /** 7331 * Translate graphic object vertically. 7332 * @param int $t_y movement to the bottom 7333 * @access public 7334 * @since 2.1.000 (2008-01-07) 7335 * @see StartTransform(), StopTransform() 7336 */ 7337 public function TranslateY($t_y) { 7338 $this->Translate(0, $t_y); 7339 } 7340 7341 /** 7342 * Translate graphic object horizontally and vertically. 7343 * @param int $t_x movement to the right 7344 * @param int $t_y movement to the bottom 7345 * @access public 7346 * @since 2.1.000 (2008-01-07) 7347 * @see StartTransform(), StopTransform() 7348 */ 7349 public function Translate($t_x, $t_y) { 7350 if ($this->rtl) { 7351 $t_x = -$t_x; 7352 } 7353 //calculate elements of transformation matrix 7354 $tm[0] = 1; 7355 $tm[1] = 0; 7356 $tm[2] = 0; 7357 $tm[3] = 1; 7358 $tm[4] = $t_x * $this->k; 7359 $tm[5] = -$t_y * $this->k; 7360 //translate the coordinate system 7361 $this->Transform($tm); 7362 } 7363 7364 /** 7365 * Rotate object. 7366 * @param float $angle angle in degrees for counter-clockwise rotation 7367 * @param int $x abscissa of the rotation center. Default is current x position 7368 * @param int $y ordinate of the rotation center. Default is current y position 7369 * @access public 7370 * @since 2.1.000 (2008-01-07) 7371 * @see StartTransform(), StopTransform() 7372 */ 7373 public function Rotate($angle, $x='', $y='') { 7374 if ($x === '') { 7375 $x = $this->x; 7376 } 7377 if ($y === '') { 7378 $y = $this->y; 7379 } 7380 if ($this->rtl) { 7381 $x = $this->w - $x; 7382 $angle = -$angle; 7383 } 7384 $y = ($this->h - $y) * $this->k; 7385 $x *= $this->k; 7386 //calculate elements of transformation matrix 7387 $tm[0] = cos(deg2rad($angle)); 7388 $tm[1] = sin(deg2rad($angle)); 7389 $tm[2] = -$tm[1]; 7390 $tm[3] = $tm[0]; 7391 $tm[4] = $x + ($tm[1] * $y) - ($tm[0] * $x); 7392 $tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x); 7393 //rotate the coordinate system around ($x,$y) 7394 $this->Transform($tm); 7395 } 7396 7397 /** 7398 * Skew horizontally. 7399 * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right) 7400 * @param int $x abscissa of the skewing center. default is current x position 7401 * @param int $y ordinate of the skewing center. default is current y position 7402 * @access public 7403 * @since 2.1.000 (2008-01-07) 7404 * @see StartTransform(), StopTransform() 7405 */ 7406 public function SkewX($angle_x, $x='', $y='') { 7407 $this->Skew($angle_x, 0, $x, $y); 7408 } 7409 7410 /** 7411 * Skew vertically. 7412 * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top) 7413 * @param int $x abscissa of the skewing center. default is current x position 7414 * @param int $y ordinate of the skewing center. default is current y position 7415 * @access public 7416 * @since 2.1.000 (2008-01-07) 7417 * @see StartTransform(), StopTransform() 7418 */ 7419 public function SkewY($angle_y, $x='', $y='') { 7420 $this->Skew(0, $angle_y, $x, $y); 7421 } 7422 7423 /** 7424 * Skew. 7425 * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right) 7426 * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top) 7427 * @param int $x abscissa of the skewing center. default is current x position 7428 * @param int $y ordinate of the skewing center. default is current y position 7429 * @access public 7430 * @since 2.1.000 (2008-01-07) 7431 * @see StartTransform(), StopTransform() 7432 */ 7433 public function Skew($angle_x, $angle_y, $x='', $y='') { 7434 if ($x === '') { 7435 $x = $this->x; 7436 } 7437 if ($y === '') { 7438 $y = $this->y; 7439 } 7440 if ($this->rtl) { 7441 $x = $this->w - $x; 7442 $angle_x = -$angle_x; 7443 } 7444 if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) { 7445 $this->Error('Please use values between -90 and +90 degrees for Skewing.'); 7446 } 7447 $x *= $this->k; 7448 $y = ($this->h - $y) * $this->k; 7449 //calculate elements of transformation matrix 7450 $tm[0] = 1; 7451 $tm[1] = tan(deg2rad($angle_y)); 7452 $tm[2] = tan(deg2rad($angle_x)); 7453 $tm[3] = 1; 7454 $tm[4] = -$tm[2] * $y; 7455 $tm[5] = -$tm[1] * $x; 7456 //skew the coordinate system 7457 $this->Transform($tm); 7458 } 7459 7460 /** 7461 * Apply graphic transformations. 7462 * @access protected 7463 * @since 2.1.000 (2008-01-07) 7464 * @see StartTransform(), StopTransform() 7465 */ 7466 protected function Transform($tm) { 7467 $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5])); 7468 // store transformation matrix 7469 $this->transfmatrix[] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]); 7470 // update tranformation mark 7471 if (end($this->transfmrk[$this->page]) !== false) { 7472 $key = key($this->transfmrk[$this->page]); 7473 $this->transfmrk[$this->page][$key] = $this->pagelen[$this->page]; 7474 } 7475 } 7476 7477 // END TRANSFORMATIONS SECTION ------------------------- 7478 7479 7480 // START GRAPHIC FUNCTIONS SECTION --------------------- 7481 // The following section is based on the code provided by David Hernandez Sanz 7482 7483 /** 7484 * 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. 7485 * @param float $width The width. 7486 * @access public 7487 * @since 1.0 7488 * @see Line(), Rect(), Cell(), MultiCell() 7489 */ 7490 public function SetLineWidth($width) { 7491 //Set line width 7492 $this->LineWidth = $width; 7493 $this->linestyleWidth = sprintf('%.2F w', ($width * $this->k)); 7494 $this->_out($this->linestyleWidth); 7495 } 7496 7497 /** 7498 * Returns the current the line width. 7499 * @return int Line width 7500 * @access public 7501 * @since 2.1.000 (2008-01-07) 7502 * @see Line(), SetLineWidth() 7503 */ 7504 public function GetLineWidth() { 7505 return $this->LineWidth; 7506 } 7507 7508 /** 7509 * Set line style. 7510 * @param array $style Line style. Array with keys among the following: 7511 * <ul> 7512 * <li>width (float): Width of the line in user units.</li> 7513 * <li>cap (string): Type of cap to put on the line. Possible values are: 7514 * butt, round, square. The difference between "square" and "butt" is that 7515 * "square" projects a flat end past the end of the line.</li> 7516 * <li>join (string): Type of join. Possible values are: miter, round, 7517 * bevel.</li> 7518 * <li>dash (mixed): Dash pattern. Is 0 (without dash) or string with 7519 * series of length values, which are the lengths of the on and off dashes. 7520 * For example: "2" represents 2 on, 2 off, 2 on, 2 off, ...; "2,1" is 2 on, 7521 * 1 off, 2 on, 1 off, ...</li> 7522 * <li>phase (integer): Modifier on the dash pattern which is used to shift 7523 * the point at which the pattern starts.</li> 7524 * <li>color (array): Draw color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K).</li> 7525 * </ul> 7526 * @access public 7527 * @since 2.1.000 (2008-01-08) 7528 */ 7529 public function SetLineStyle($style) { 7530 extract($style); 7531 if (isset($width)) { 7532 $width_prev = $this->LineWidth; 7533 $this->SetLineWidth($width); 7534 $this->LineWidth = $width_prev; 7535 } 7536 if (isset($cap)) { 7537 $ca = array('butt' => 0, 'round'=> 1, 'square' => 2); 7538 if (isset($ca[$cap])) { 7539 $this->linestyleCap = $ca[$cap].' J'; 7540 $this->_out($this->linestyleCap); 7541 } 7542 } 7543 if (isset($join)) { 7544 $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2); 7545 if (isset($ja[$join])) { 7546 $this->linestyleJoin = $ja[$join].' j'; 7547 $this->_out($this->linestyleJoin); 7548 } 7549 } 7550 if (isset($dash)) { 7551 $dash_string = ''; 7552 if ($dash) { 7553 if (ereg('^.+,', $dash)) { 7554 $tab = explode(',', $dash); 7555 } else { 7556 $tab = array($dash); 7557 } 7558 $dash_string = ''; 7559 foreach ($tab as $i => $v) { 7560 if ($i) { 7561 $dash_string .= ' '; 7562 } 7563 $dash_string .= sprintf("%.2F", $v); 7564 } 7565 } 7566 if (!isset($phase) OR !$dash) { 7567 $phase = 0; 7568 } 7569 $this->linestyleDash = sprintf("[%s] %.2F d", $dash_string, $phase); 7570 $this->_out($this->linestyleDash); 7571 } 7572 if (isset($color)) { 7573 $this->SetDrawColorArray($color); 7574 } 7575 } 7576 7577 /* 7578 * Set a draw point. 7579 * @param float $x Abscissa of point. 7580 * @param float $y Ordinate of point. 7581 * @access protected 7582 * @since 2.1.000 (2008-01-08) 7583 */ 7584 protected function _outPoint($x, $y) { 7585 if ($this->rtl) { 7586 $x = $this->w - $x; 7587 } 7588 $this->_out(sprintf("%.2F %.2F m", $x * $this->k, ($this->h - $y) * $this->k)); 7589 } 7590 7591 /* 7592 * Draws a line from last draw point. 7593 * @param float $x Abscissa of end point. 7594 * @param float $y Ordinate of end point. 7595 * @access protected 7596 * @since 2.1.000 (2008-01-08) 7597 */ 7598 protected function _outLine($x, $y) { 7599 if ($this->rtl) { 7600 $x = $this->w - $x; 7601 } 7602 $this->_out(sprintf("%.2F %.2F l", $x * $this->k, ($this->h - $y) * $this->k)); 7603 } 7604 7605 /** 7606 * Draws a rectangle. 7607 * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language). 7608 * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language). 7609 * @param float $w Width. 7610 * @param float $h Height. 7611 * @param string $op options 7612 * @access protected 7613 * @since 2.1.000 (2008-01-08) 7614 */ 7615 protected function _outRect($x, $y, $w, $h, $op) { 7616 if ($this->rtl) { 7617 $x = $this->w - $x - $w; 7618 } 7619 $this->_out(sprintf('%.2F %.2F %.2F %.2F re %s', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k, $op)); 7620 } 7621 7622 /* 7623 * Draws a Bezier curve from last draw point. 7624 * The Bezier curve is a tangent to the line between the control points at either end of the curve. 7625 * @param float $x1 Abscissa of control point 1. 7626 * @param float $y1 Ordinate of control point 1. 7627 * @param float $x2 Abscissa of control point 2. 7628 * @param float $y2 Ordinate of control point 2. 7629 * @param float $x3 Abscissa of end point. 7630 * @param float $y3 Ordinate of end point. 7631 * @access protected 7632 * @since 2.1.000 (2008-01-08) 7633 */ 7634 protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) { 7635 if ($this->rtl) { 7636 $x1 = $this->w - $x1; 7637 $x2 = $this->w - $x2; 7638 $x3 = $this->w - $x3; 7639 } 7640 $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)); 7641 } 7642 7643 /** 7644 * Draws a line between two points. 7645 * @param float $x1 Abscissa of first point. 7646 * @param float $y1 Ordinate of first point. 7647 * @param float $x2 Abscissa of second point. 7648 * @param float $y2 Ordinate of second point. 7649 * @param array $style Line style. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). 7650 * @access public 7651 * @since 1.0 7652 * @see SetLineWidth(), SetDrawColor(), SetLineStyle() 7653 */ 7654 public function Line($x1, $y1, $x2, $y2, $style=array()) { 7655 if ($style) { 7656 $this->SetLineStyle($style); 7657 } 7658 $this->_outPoint($x1, $y1); 7659 $this->_outLine($x2, $y2); 7660 $this->_out(' S'); 7661 } 7662 7663 /** 7664 * Draws a rectangle. 7665 * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language). 7666 * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language). 7667 * @param float $w Width. 7668 * @param float $h Height. 7669 * @param string $style Style of rendering. Possible values are: 7670 * <ul> 7671 * <li>D or empty string: Draw (default).</li> 7672 * <li>F: Fill.</li> 7673 * <li>DF or FD: Draw and fill.</li> 7674 * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li> 7675 * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li> 7676 * </ul> 7677 * @param array $border_style Border style of rectangle. Array with keys among the following: 7678 * <ul> 7679 * <li>all: Line style of all borders. Array like for {@link SetLineStyle SetLineStyle}.</li> 7680 * <li>L, T, R, B or combinations: Line style of left, top, right or bottom border. Array like for {@link SetLineStyle SetLineStyle}.</li> 7681 * </ul> 7682 * If a key is not present or is null, not draws the border. Default value: default line style (empty array). 7683 * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). 7684 * @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). 7685 * @access public 7686 * @since 1.0 7687 * @see SetLineStyle() 7688 */ 7689 public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) { 7690 if (!(false === strpos($style, 'F')) AND isset($fill_color)) { 7691 $this->SetFillColorArray($fill_color); 7692 } 7693 switch ($style) { 7694 case 'F': { 7695 $op = 'f'; 7696 $border_style = array(); 7697 $this->_outRect($x, $y, $w, $h, $op); 7698 break; 7699 } 7700 case 'DF': 7701 case 'FD': { 7702 if ((!$border_style) OR (isset($border_style['all']))) { 7703 $op = 'B'; 7704 if (isset($border_style['all'])) { 7705 $this->SetLineStyle($border_style['all']); 7706 $border_style = array(); 7707 } 7708 } else { 7709 $op = 'f'; 7710 } 7711 $this->_outRect($x, $y, $w, $h, $op); 7712 break; 7713 } 7714 case 'CNZ': { 7715 $op = 'W n'; 7716 $this->_outRect($x, $y, $w, $h, $op); 7717 break; 7718 } 7719 case 'CEO': { 7720 $op = 'W* n'; 7721 $this->_outRect($x, $y, $w, $h, $op); 7722 break; 7723 } 7724 default: { 7725 $op = 'S'; 7726 if ((!$border_style) OR (isset($border_style['all']))) { 7727 if (isset($border_style['all']) AND $border_style['all']) { 7728 $this->SetLineStyle($border_style['all']); 7729 $border_style = array(); 7730 } 7731 $this->_outRect($x, $y, $w, $h, $op); 7732 } 7733 break; 7734 } 7735 } 7736 if ($border_style) { 7737 $border_style2 = array(); 7738 foreach ($border_style as $line => $value) { 7739 $lenght = strlen($line); 7740 for ($i = 0; $i < $lenght; ++$i) { 7741 $border_style2[$line[$i]] = $value; 7742 } 7743 } 7744 $border_style = $border_style2; 7745 if (isset($border_style['L']) AND $border_style['L']) { 7746 $this->Line($x, $y, $x, $y + $h, $border_style['L']); 7747 } 7748 if (isset($border_style['T']) AND $border_style['T']) { 7749 $this->Line($x, $y, $x + $w, $y, $border_style['T']); 7750 } 7751 if (isset($border_style['R']) AND $border_style['R']) { 7752 $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style['R']); 7753 } 7754 if (isset($border_style['B']) AND $border_style['B']) { 7755 $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style['B']); 7756 } 7757 } 7758 } 7759 7760 7761 /** 7762 * Draws a Bezier curve. 7763 * The Bezier curve is a tangent to the line between the control points at 7764 * either end of the curve. 7765 * @param float $x0 Abscissa of start point. 7766 * @param float $y0 Ordinate of start point. 7767 * @param float $x1 Abscissa of control point 1. 7768 * @param float $y1 Ordinate of control point 1. 7769 * @param float $x2 Abscissa of control point 2. 7770 * @param float $y2 Ordinate of control point 2. 7771 * @param float $x3 Abscissa of end point. 7772 * @param float $y3 Ordinate of end point. 7773 * @param string $style Style of rendering. Possible values are: 7774 * <ul> 7775 * <li>D or empty string: Draw (default).</li> 7776 * <li>F: Fill.</li> 7777 * <li>DF or FD: Draw and fill.</li> 7778 * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li> 7779 * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li> 7780 * </ul> 7781 * @param array $line_style Line style of curve. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). 7782 * @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). 7783 * @access public 7784 * @see SetLineStyle() 7785 * @since 2.1.000 (2008-01-08) 7786 */ 7787 public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) { 7788 if (!(false === strpos($style, 'F')) AND isset($fill_color)) { 7789 $this->SetFillColorArray($fill_color); 7790 } 7791 switch ($style) { 7792 case 'F': { 7793 $op = 'f'; 7794 $line_style = array(); 7795 break; 7796 } 7797 case 'FD': 7798 case 'DF': { 7799 $op = 'B'; 7800 break; 7801 } 7802 case 'CNZ': { 7803 $op = 'W n'; 7804 break; 7805 } 7806 case 'CEO': { 7807 $op = 'W* n'; 7808 break; 7809 } 7810 default: { 7811 $op = 'S'; 7812 break; 7813 } 7814 } 7815 if ($line_style) { 7816 $this->SetLineStyle($line_style); 7817 } 7818 $this->_outPoint($x0, $y0); 7819 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3); 7820 $this->_out($op); 7821 } 7822 7823 /** 7824 * Draws a poly-Bezier curve. 7825 * Each Bezier curve segment is a tangent to the line between the control points at 7826 * either end of the curve. 7827 * @param float $x0 Abscissa of start point. 7828 * @param float $y0 Ordinate of start point. 7829 * @param float $segments An array of bezier descriptions. Format: array(x1, y1, x2, y2, x3, y3). 7830 * @param string $style Style of rendering. Possible values are: 7831 * <ul> 7832 * <li>D or empty string: Draw (default).</li> 7833 * <li>F: Fill.</li> 7834 * <li>DF or FD: Draw and fill.</li> 7835 * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li> 7836 * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li> 7837 * </ul> 7838 * @param array $line_style Line style of curve. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). 7839 * @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). 7840 * @access public 7841 * @see SetLineStyle() 7842 * @since 3.0008 (2008-05-12) 7843 */ 7844 public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) { 7845 if (!(false === strpos($style, 'F')) AND isset($fill_color)) { 7846 $this->SetFillColorArray($fill_color); 7847 } 7848 switch ($style) { 7849 case 'F': { 7850 $op = 'f'; 7851 $line_style = array(); 7852 break; 7853 } 7854 case 'FD': 7855 case 'DF': { 7856 $op = 'B'; 7857 break; 7858 } 7859 case 'CNZ': { 7860 $op = 'W n'; 7861 break; 7862 } 7863 case 'CEO': { 7864 $op = 'W* n'; 7865 break; 7866 } 7867 default: { 7868 $op = 'S'; 7869 break; 7870 } 7871 } 7872 if ($line_style) { 7873 $this->SetLineStyle($line_style); 7874 } 7875 $this->_outPoint($x0, $y0); 7876 foreach ($segments as $segment) { 7877 list($x1, $y1, $x2, $y2, $x3, $y3) = $segment; 7878 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3); 7879 } 7880 $this->_out($op); 7881 } 7882 7883 /** 7884 * Draws an ellipse. 7885 * An ellipse is formed from n Bezier curves. 7886 * @param float $x0 Abscissa of center point. 7887 * @param float $y0 Ordinate of center point. 7888 * @param float $rx Horizontal radius. 7889 * @param float $ry Vertical radius (if ry = 0 then is a circle, see {@link Circle Circle}). Default value: 0. 7890 * @param float $angle: Angle oriented (anti-clockwise). Default value: 0. 7891 * @param float $astart: Angle start of draw line. Default value: 0. 7892 * @param float $afinish: Angle finish of draw line. Default value: 360. 7893 * @param string $style Style of rendering. Possible values are: 7894 * <ul> 7895 * <li>D or empty string: Draw (default).</li> 7896 * <li>F: Fill.</li> 7897 * <li>DF or FD: Draw and fill.</li> 7898 * <li>C: Draw close.</li> 7899 * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li> 7900 * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li> 7901 * </ul> 7902 * @param array $line_style Line style of ellipse. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). 7903 * @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). 7904 * @param integer $nc Number of curves used in ellipse. Default value: 8. 7905 * @access public 7906 * @since 2.1.000 (2008-01-08) 7907 */ 7908 public function Ellipse($x0, $y0, $rx, $ry=0, $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=8) { 7909 if ($angle) { 7910 $this->StartTransform(); 7911 $this->Rotate($angle, $x0, $y0); 7912 $this->Ellipse($x0, $y0, $rx, $ry, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc); 7913 $this->StopTransform(); 7914 return; 7915 } 7916 if ($rx) { 7917 if (!(false === strpos($style, 'F')) AND isset($fill_color)) { 7918 $this->SetFillColorArray($fill_color); 7919 } 7920 switch ($style) { 7921 case 'F': { 7922 $op = 'f'; 7923 $line_style = array(); 7924 break; 7925 } 7926 case 'FD': 7927 case 'DF': { 7928 $op = 'B'; 7929 break; 7930 } 7931 case 'C': { 7932 $op = 's'; // Small 's' signifies closing the path as well 7933 break; 7934 } 7935 case 'CNZ': { 7936 $op = 'W n'; 7937 break; 7938 } 7939 case 'CEO': { 7940 $op = 'W* n'; 7941 break; 7942 } 7943 default: { 7944 $op = 'S'; 7945 break; 7946 } 7947 } 7948 if ($line_style) { 7949 $this->SetLineStyle($line_style); 7950 } 7951 if (!$ry) { 7952 $ry = $rx; 7953 } 7954 $rx *= $this->k; 7955 $ry *= $this->k; 7956 if ($nc < 2) { 7957 $nc = 2; 7958 } 7959 $astart = deg2rad((float) $astart); 7960 $afinish = deg2rad((float) $afinish); 7961 $total_angle = $afinish - $astart; 7962 $dt = $total_angle / $nc; 7963 $dtm = $dt / 3; 7964 $x0 *= $this->k; 7965 $y0 = ($this->h - $y0) * $this->k; 7966 $t1 = $astart; 7967 $a0 = $x0 + ($rx * cos($t1)); 7968 $b0 = $y0 + ($ry * sin($t1)); 7969 $c0 = -$rx * sin($t1); 7970 $d0 = $ry * cos($t1); 7971 $this->_outPoint($a0 / $this->k, $this->h - ($b0 / $this->k)); 7972 for ($i = 1; $i <= $nc; ++$i) { 7973 // Draw this bit of the total curve 7974 $t1 = ($i * $dt) + $astart; 7975 $a1 = $x0 + ($rx * cos($t1)); 7976 $b1 = $y0 + ($ry * sin($t1)); 7977 $c1 = -$rx * sin($t1); 7978 $d1 = $ry * cos($t1); 7979 $this->_outCurve(($a0 + ($c0 * $dtm)) / $this->k, $this->h - (($b0 + ($d0 * $dtm)) / $this->k), ($a1 - ($c1 * $dtm)) / $this->k, $this->h - (($b1 - ($d1 * $dtm)) / $this->k), $a1 / $this->k, $this->h - ($b1 / $this->k)); 7980 $a0 = $a1; 7981 $b0 = $b1; 7982 $c0 = $c1; 7983 $d0 = $d1; 7984 } 7985 $this->_out($op); 7986 } 7987 } 7988 7989 /** 7990 * Draws a circle. 7991 * A circle is formed from n Bezier curves. 7992 * @param float $x0 Abscissa of center point. 7993 * @param float $y0 Ordinate of center point. 7994 * @param float $r Radius. 7995 * @param float $astart: Angle start of draw line. Default value: 0. 7996 * @param float $afinish: Angle finish of draw line. Default value: 360. 7997 * @param string $style Style of rendering. Possible values are: 7998 * <ul> 7999 * <li>D or empty string: Draw (default).</li> 8000 * <li>F: Fill.</li> 8001 * <li>DF or FD: Draw and fill.</li> 8002 * <li>C: Draw close.</li> 8003 * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li> 8004 * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li> 8005 * </ul> 8006 * @param array $line_style Line style of circle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). 8007 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). 8008 * @param integer $nc Number of curves used in circle. Default value: 8. 8009 * @access public 8010 * @since 2.1.000 (2008-01-08) 8011 */ 8012 public function Circle($x0, $y0, $r, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=8) { 8013 $this->Ellipse($x0, $y0, $r, 0, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc); 8014 } 8015 8016 /** 8017 * Draws a polygon. 8018 * @param array $p Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1)) 8019 * @param string $style Style of rendering. Possible values are: 8020 * <ul> 8021 * <li>D or empty string: Draw (default).</li> 8022 * <li>F: Fill.</li> 8023 * <li>DF or FD: Draw and fill.</li> 8024 * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li> 8025 * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li> 8026 * </ul> 8027 * @param array $line_style Line style of polygon. Array with keys among the following: 8028 * <ul> 8029 * <li>all: Line style of all lines. Array like for {@link SetLineStyle SetLineStyle}.</li> 8030 * <li>0 to ($np - 1): Line style of each line. Array like for {@link SetLineStyle SetLineStyle}.</li> 8031 * </ul> 8032 * If a key is not present or is null, not draws the line. Default value is default line style (empty array). 8033 * @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). 8034 * @access public 8035 * @since 2.1.000 (2008-01-08) 8036 */ 8037 public function Polygon($p, $style='', $line_style=array(), $fill_color=array()) { 8038 $np = count($p) / 2; 8039 if (!(false === strpos($style, 'F')) AND isset($fill_color)) { 8040 $this->SetFillColorArray($fill_color); 8041 } 8042 switch ($style) { 8043 case 'F': { 8044 $line_style = array(); 8045 $op = 'f'; 8046 break; 8047 } 8048 case 'FD': 8049 case 'DF': { 8050 $op = 'B'; 8051 break; 8052 } 8053 case 'CNZ': { 8054 $op = 'W n'; 8055 break; 8056 } 8057 case 'CEO': { 8058 $op = 'W* n'; 8059 break; 8060 } 8061 default: { 8062 $op = 'S'; 8063 break; 8064 } 8065 } 8066 $draw = true; 8067 if ($line_style) { 8068 if (isset($line_style['all'])) { 8069 $this->SetLineStyle($line_style['all']); 8070 } else { // 0 .. (np - 1), op = {B, S} 8071 $draw = false; 8072 if ('B' == $op) { 8073 $op = 'f'; 8074 $this->_outPoint($p[0], $p[1]); 8075 for ($i = 2; $i < ($np * 2); $i = $i + 2) { 8076 $this->_outLine($p[$i], $p[$i + 1]); 8077 } 8078 $this->_outLine($p[0], $p[1]); 8079 $this->_out($op); 8080 } 8081 $p[($np * 2)] = $p[0]; 8082 $p[(($np * 2) + 1)] = $p[1]; 8083 for ($i = 0; $i < $np; ++$i) { 8084 if (isset($line_style[$i]) AND ($line_style[$i] != 0)) { 8085 $this->Line($p[($i * 2)], $p[(($i * 2) + 1)], $p[(($i * 2) + 2)], $p[(($i * 2) + 3)], $line_style[$i]); 8086 } 8087 } 8088 } 8089 } 8090 if ($draw) { 8091 $this->_outPoint($p[0], $p[1]); 8092 for ($i = 2; $i < ($np * 2); $i = $i + 2) { 8093 $this->_outLine($p[$i], $p[$i + 1]); 8094 } 8095 $this->_outLine($p[0], $p[1]); 8096 $this->_out($op); 8097 } 8098 } 8099 8100 /** 8101 * Draws a regular polygon. 8102 * @param float $x0 Abscissa of center point. 8103 * @param float $y0 Ordinate of center point. 8104 * @param float $r: Radius of inscribed circle. 8105 * @param integer $ns Number of sides. 8106 * @param float $angle Angle oriented (anti-clockwise). Default value: 0. 8107 * @param boolean $draw_circle Draw inscribed circle or not. Default value: false. 8108 * @param string $style Style of rendering. Possible values are: 8109 * <ul> 8110 * <li>D or empty string: Draw (default).</li> 8111 * <li>F: Fill.</li> 8112 * <li>DF or FD: Draw and fill.</li> 8113 * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li> 8114 * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li> 8115 * </ul> 8116 * @param array $line_style Line style of polygon sides. Array with keys among the following: 8117 * <ul> 8118 * <li>all: Line style of all sides. Array like for {@link SetLineStyle SetLineStyle}.</li> 8119 * <li>0 to ($ns - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li> 8120 * </ul> 8121 * If a key is not present or is null, not draws the side. Default value is default line style (empty array). 8122 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). 8123 * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are: 8124 * <ul> 8125 * <li>D or empty string: Draw (default).</li> 8126 * <li>F: Fill.</li> 8127 * <li>DF or FD: Draw and fill.</li> 8128 * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li> 8129 * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li> 8130 * </ul> 8131 * @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). 8132 * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array). 8133 * @access public 8134 * @since 2.1.000 (2008-01-08) 8135 */ 8136 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()) { 8137 if (3 > $ns) { 8138 $ns = 3; 8139 } 8140 if ($draw_circle) { 8141 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color); 8142 } 8143 $p = array(); 8144 for ($i = 0; $i < $ns; ++$i) { 8145 $a = $angle + ($i * 360 / $ns); 8146 $a_rad = deg2rad((float) $a); 8147 $p[] = $x0 + ($r * sin($a_rad)); 8148 $p[] = $y0 + ($r * cos($a_rad)); 8149 } 8150 $this->Polygon($p, $style, $line_style, $fill_color); 8151 } 8152 8153 /** 8154 * Draws a star polygon 8155 * @param float $x0 Abscissa of center point. 8156 * @param float $y0 Ordinate of center point. 8157 * @param float $r Radius of inscribed circle. 8158 * @param integer $nv Number of vertices. 8159 * @param integer $ng Number of gap (if ($ng % $nv = 1) then is a regular polygon). 8160 * @param float $angle: Angle oriented (anti-clockwise). Default value: 0. 8161 * @param boolean $draw_circle: Draw inscribed circle or not. Default value is false. 8162 * @param string $style Style of rendering. Possible values are: 8163 * <ul> 8164 * <li>D or empty string: Draw (default).</li> 8165 * <li>F: Fill.</li> 8166 * <li>DF or FD: Draw and fill.</li> 8167 * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li> 8168 * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li> 8169 * </ul> 8170 * @param array $line_style Line style of polygon sides. Array with keys among the following: 8171 * <ul> 8172 * <li>all: Line style of all sides. Array like for 8173 * {@link SetLineStyle SetLineStyle}.</li> 8174 * <li>0 to (n - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li> 8175 * </ul> 8176 * If a key is not present or is null, not draws the side. Default value is default line style (empty array). 8177 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). 8178 * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are: 8179 * <ul> 8180 * <li>D or empty string: Draw (default).</li> 8181 * <li>F: Fill.</li> 8182 * <li>DF or FD: Draw and fill.</li> 8183 * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li> 8184 * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li> 8185 * </ul> 8186 * @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). 8187 * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array). 8188 * @access public 8189 * @since 2.1.000 (2008-01-08) 8190 */ 8191 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()) { 8192 if (2 > $nv) { 8193 $nv = 2; 8194 } 8195 if ($draw_circle) { 8196 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color); 8197 } 8198 $p2 = array(); 8199 $visited = array(); 8200 for ($i = 0; $i < $nv; ++$i) { 8201 $a = $angle + ($i * 360 / $nv); 8202 $a_rad = deg2rad((float) $a); 8203 $p2[] = $x0 + ($r * sin($a_rad)); 8204 $p2[] = $y0 + ($r * cos($a_rad)); 8205 $visited[] = false; 8206 } 8207 $p = array(); 8208 $i = 0; 8209 do { 8210 $p[] = $p2[$i * 2]; 8211 $p[] = $p2[($i * 2) + 1]; 8212 $visited[$i] = true; 8213 $i += $ng; 8214 $i %= $nv; 8215 } while (!$visited[$i]); 8216 $this->Polygon($p, $style, $line_style, $fill_color); 8217 } 8218 8219 /** 8220 * Draws a rounded rectangle. 8221 * @param float $x Abscissa of upper-left corner. 8222 * @param float $y Ordinate of upper-left corner. 8223 * @param float $w Width. 8224 * @param float $h Height. 8225 * @param float $r Radius of the rounded corners. 8226 * @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"). 8227 * @param string $style Style of rendering. Possible values are: 8228 * <ul> 8229 * <li>D or empty string: Draw (default).</li> 8230 * <li>F: Fill.</li> 8231 * <li>DF or FD: Draw and fill.</li> 8232 * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li> 8233 * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li> 8234 * </ul> 8235 * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). 8236 * @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). 8237 * @access public 8238 * @since 2.1.000 (2008-01-08) 8239 */ 8240 public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) { 8241 if ('0000' == $round_corner) { // Not rounded 8242 $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color); 8243 } else { // Rounded 8244 if (!(false === strpos($style, 'F')) AND isset($fill_color)) { 8245 $this->SetFillColorArray($fill_color); 8246 } 8247 switch ($style) { 8248 case 'F': { 8249 $border_style = array(); 8250 $op = 'f'; 8251 break; 8252 } 8253 case 'FD': 8254 case 'DF': { 8255 $op = 'B'; 8256 break; 8257 } 8258 case 'CNZ': { 8259 $op = 'W n'; 8260 break; 8261 } 8262 case 'CEO': { 8263 $op = 'W* n'; 8264 break; 8265 } 8266 default: { 8267 $op = 'S'; 8268 break; 8269 } 8270 } 8271 if ($border_style) { 8272 $this->SetLineStyle($border_style); 8273 } 8274 $MyArc = 4 / 3 * (sqrt(2) - 1); 8275 $this->_outPoint($x + $r, $y); 8276 $xc = $x + $w - $r; 8277 $yc = $y + $r; 8278 $this->_outLine($xc, $y); 8279 if ($round_corner[0]) { 8280 $this->_outCurve($xc + ($r * $MyArc), $yc - $r, $xc + $r, $yc - ($r * $MyArc), $xc + $r, $yc); 8281 } else { 8282 $this->_outLine($x + $w, $y); 8283 } 8284 $xc = $x + $w - $r; 8285 $yc = $y + $h - $r; 8286 $this->_outLine($x + $w, $yc); 8287 if ($round_corner[1]) { 8288 $this->_outCurve($xc + $r, $yc + ($r * $MyArc), $xc + ($r * $MyArc), $yc + $r, $xc, $yc + $r); 8289 } else { 8290 $this->_outLine($x + $w, $y + $h); 8291 } 8292 $xc = $x + $r; 8293 $yc = $y + $h - $r; 8294 $this->_outLine($xc, $y + $h); 8295 if ($round_corner[2]) { 8296 $this->_outCurve($xc - ($r * $MyArc), $yc + $r, $xc - $r, $yc + ($r * $MyArc), $xc - $r, $yc); 8297 } else { 8298 $this->_outLine($x, $y + $h); 8299 } 8300 $xc = $x + $r; 8301 $yc = $y + $r; 8302 $this->_outLine($x, $yc); 8303 if ($round_corner[3]) { 8304 $this->_outCurve($xc - $r, $yc - ($r * $MyArc), $xc - ($r * $MyArc), $yc - $r, $xc, $yc - $r); 8305 } else { 8306 $this->_outLine($x, $y); 8307 $this->_outLine($x + $r, $y); 8308 } 8309 $this->_out($op); 8310 } 8311 } 8312 8313 // END GRAPHIC FUNCTIONS SECTION ----------------------- 8314 8315 // BIDIRECTIONAL TEXT SECTION -------------------------- 8316 /** 8317 * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/). 8318 * @param string $str string to manipulate. 8319 * @param bool $forcertl if 'R' forces RTL, if 'L' forces LTR 8320 * @return string 8321 * @access protected 8322 * @author Nicola Asuni 8323 * @since 2.1.000 (2008-01-08) 8324 */ 8325 protected function utf8StrRev($str, $setbom=false, $forcertl=false) { 8326 return $this->arrUTF8ToUTF16BE($this->utf8Bidi($this->UTF8StringToArray($str), $str, $forcertl), $setbom); 8327 } 8328 8329 /** 8330 * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/). 8331 * @param array $ta array of characters composing the string. 8332 * @param string $str string to process 8333 * @param bool $forcertl if 'R' forces RTL, if 'L' forces LTR 8334 * @return string 8335 * @author Nicola Asuni 8336 * @access protected 8337 * @since 2.4.000 (2008-03-06) 8338 */ 8339 protected function utf8Bidi($ta, $str='', $forcertl=false) { 8340 global $unicode, $unicode_mirror, $unicode_arlet, $laa_array, $diacritics; 8341 // paragraph embedding level 8342 $pel = 0; 8343 // max level 8344 $maxlevel = 0; 8345 if ($this->empty_string($str)) { 8346 // create string from array 8347 $str = $this->UTF8ArrSubString($ta); 8348 } 8349 // check if string contains arabic text 8350 if (preg_match(K_RE_PATTERN_ARABIC, $str)) { 8351 $arabic = true; 8352 } else { 8353 $arabic = false; 8354 } 8355 // check if string contains RTL text 8356 if (!($forcertl OR $arabic OR preg_match(K_RE_PATTERN_RTL, $str))) { 8357 return $ta; 8358 } 8359 8360 // get number of chars 8361 $numchars = count($ta); 8362 8363 if ($forcertl == 'R') { 8364 $pel = 1; 8365 } elseif ($forcertl == 'L') { 8366 $pel = 0; 8367 } else { 8368 // P2. In each paragraph, find the first character of type L, AL, or R. 8369 // 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. 8370 for ($i=0; $i < $numchars; ++$i) { 8371 $type = $unicode[$ta[$i]]; 8372 if ($type == 'L') { 8373 $pel = 0; 8374 break; 8375 } elseif (($type == 'AL') OR ($type == 'R')) { 8376 $pel = 1; 8377 break; 8378 } 8379 } 8380 } 8381 8382 // Current Embedding Level 8383 $cel = $pel; 8384 // directional override status 8385 $dos = 'N'; 8386 $remember = array(); 8387 // start-of-level-run 8388 $sor = $pel % 2 ? 'R' : 'L'; 8389 $eor = $sor; 8390 8391 // Array of characters data 8392 $chardata = Array(); 8393 8394 // 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. 8395 // In the resolution of levels in rules I1 and I2, the maximum embedding level of 62 can be reached. 8396 for ($i=0; $i < $numchars; ++$i) { 8397 if ($ta[$i] == K_RLE) { 8398 // X2. With each RLE, compute the least greater odd embedding level. 8399 // 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. 8400 // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status. 8401 $next_level = $cel + ($cel % 2) + 1; 8402 if ($next_level < 62) { 8403 $remember[] = array('num' => K_RLE, 'cel' => $cel, 'dos' => $dos); 8404 $cel = $next_level; 8405 $dos = 'N'; 8406 $sor = $eor; 8407 $eor = $cel % 2 ? 'R' : 'L'; 8408 } 8409 } elseif ($ta[$i] == K_LRE) { 8410 // X3. With each LRE, compute the least greater even embedding level. 8411 // 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. 8412 // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status. 8413 $next_level = $cel + 2 - ($cel % 2); 8414 if ( $next_level < 62 ) { 8415 $remember[] = array('num' => K_LRE, 'cel' => $cel, 'dos' => $dos); 8416 $cel = $next_level; 8417 $dos = 'N'; 8418 $sor = $eor; 8419 $eor = $cel % 2 ? 'R' : 'L'; 8420 } 8421 } elseif ($ta[$i] == K_RLO) { 8422 // X4. With each RLO, compute the least greater odd embedding level. 8423 // 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. 8424 // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status. 8425 $next_level = $cel + ($cel % 2) + 1; 8426 if ($next_level < 62) { 8427 $remember[] = array('num' => K_RLO, 'cel' => $cel, 'dos' => $dos); 8428 $cel = $next_level; 8429 $dos = 'R'; 8430 $sor = $eor; 8431 $eor = $cel % 2 ? 'R' : 'L'; 8432 } 8433 } elseif ($ta[$i] == K_LRO) { 8434 // X5. With each LRO, compute the least greater even embedding level. 8435 // 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. 8436 // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status. 8437 $next_level = $cel + 2 - ($cel % 2); 8438 if ( $next_level < 62 ) { 8439 $remember[] = array('num' => K_LRO, 'cel' => $cel, 'dos' => $dos); 8440 $cel = $next_level; 8441 $dos = 'L'; 8442 $sor = $eor; 8443 $eor = $cel % 2 ? 'R' : 'L'; 8444 } 8445 } elseif ($ta[$i] == K_PDF) { 8446 // 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. 8447 if (count($remember)) { 8448 $last = count($remember ) - 1; 8449 if (($remember[$last]['num'] == K_RLE) OR 8450 ($remember[$last]['num'] == K_LRE) OR 8451 ($remember[$last]['num'] == K_RLO) OR 8452 ($remember[$last]['num'] == K_LRO)) { 8453 $match = array_pop($remember); 8454 $cel = $match['cel']; 8455 $dos = $match['dos']; 8456 $sor = $eor; 8457 $eor = ($cel > $match['cel'] ? $cel : $match['cel']) % 2 ? 'R' : 'L'; 8458 } 8459 } 8460 } elseif (($ta[$i] != K_RLE) AND 8461 ($ta[$i] != K_LRE) AND 8462 ($ta[$i] != K_RLO) AND 8463 ($ta[$i] != K_LRO) AND 8464 ($ta[$i] != K_PDF)) { 8465 // X6. For all types besides RLE, LRE, RLO, LRO, and PDF: 8466 // a. Set the level of the current character to the current embedding level. 8467 // b. Whenever the directional override status is not neutral, reset the current character type to the directional override status. 8468 if ($dos != 'N') { 8469 $chardir = $dos; 8470 } else { 8471 $chardir = $unicode[$ta[$i]]; 8472 } 8473 // stores string characters and other information 8474 $chardata[] = array('char' => $ta[$i], 'level' => $cel, 'type' => $chardir, 'sor' => $sor, 'eor' => $eor); 8475 } 8476 } // end for each char 8477 8478 // X8. All explicit directional embeddings and overrides are completely terminated at the end of each paragraph. Paragraph separators are not included in the embedding. 8479 // X9. Remove all RLE, LRE, RLO, LRO, PDF, and BN codes. 8480 // 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. 8481 8482 // 3.3.3 Resolving Weak Types 8483 // 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. 8484 // Nonspacing marks are now resolved based on the previous characters. 8485 $numchars = count($chardata); 8486 8487 // 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. 8488 $prevlevel = -1; // track level changes 8489 $levcount = 0; // counts consecutive chars at the same level 8490 for ($i=0; $i < $numchars; ++$i) { 8491 if ($chardata[$i]['type'] == 'NSM') { 8492 if ($levcount) { 8493 $chardata[$i]['type'] = $chardata[$i]['sor']; 8494 } elseif ($i > 0) { 8495 $chardata[$i]['type'] = $chardata[($i-1)]['type']; 8496 } 8497 } 8498 if ($chardata[$i]['level'] != $prevlevel) { 8499 $levcount = 0; 8500 } else { 8501 ++$levcount; 8502 } 8503 $prevlevel = $chardata[$i]['level']; 8504 } 8505 8506 // 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. 8507 $prevlevel = -1; 8508 $levcount = 0; 8509 for ($i=0; $i < $numchars; ++$i) { 8510 if ($chardata[$i]['char'] == 'EN') { 8511 for ($j=$levcount; $j >= 0; $j--) { 8512 if ($chardata[$j]['type'] == 'AL') { 8513 $chardata[$i]['type'] = 'AN'; 8514 } elseif (($chardata[$j]['type'] == 'L') OR ($chardata[$j]['type'] == 'R')) { 8515 break; 8516 } 8517 } 8518 } 8519 if ($chardata[$i]['level'] != $prevlevel) { 8520 $levcount = 0; 8521 } else { 8522 ++$levcount; 8523 } 8524 $prevlevel = $chardata[$i]['level']; 8525 } 8526 8527 // W3. Change all ALs to R. 8528 for ($i=0; $i < $numchars; ++$i) { 8529 if ($chardata[$i]['type'] == 'AL') { 8530 $chardata[$i]['type'] = 'R'; 8531 } 8532 } 8533 8534 // 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. 8535 $prevlevel = -1; 8536 $levcount = 0; 8537 for ($i=0; $i < $numchars; ++$i) { 8538 if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) { 8539 if (($chardata[$i]['type'] == 'ES') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) { 8540 $chardata[$i]['type'] = 'EN'; 8541 } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) { 8542 $chardata[$i]['type'] = 'EN'; 8543 } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'AN') AND ($chardata[($i+1)]['type'] == 'AN')) { 8544 $chardata[$i]['type'] = 'AN'; 8545 } 8546 } 8547 if ($chardata[$i]['level'] != $prevlevel) { 8548 $levcount = 0; 8549 } else { 8550 ++$levcount; 8551 } 8552 $prevlevel = $chardata[$i]['level']; 8553 } 8554 8555 // W5. A sequence of European terminators adjacent to European numbers changes to all European numbers. 8556 $prevlevel = -1; 8557 $levcount = 0; 8558 for ($i=0; $i < $numchars; ++$i) { 8559 if ($chardata[$i]['type'] == 'ET') { 8560 if (($levcount > 0) AND ($chardata[($i-1)]['type'] == 'EN')) { 8561 $chardata[$i]['type'] = 'EN'; 8562 } else { 8563 $j = $i+1; 8564 while (($j < $numchars) AND ($chardata[$j]['level'] == $prevlevel)) { 8565 if ($chardata[$j]['type'] == 'EN') { 8566 $chardata[$i]['type'] = 'EN'; 8567 break; 8568 } elseif ($chardata[$j]['type'] != 'ET') { 8569 break; 8570 } 8571 ++$j; 8572 } 8573 } 8574 } 8575 if ($chardata[$i]['level'] != $prevlevel) { 8576 $levcount = 0; 8577 } else { 8578 ++$levcount; 8579 } 8580 $prevlevel = $chardata[$i]['level']; 8581 } 8582 8583 // W6. Otherwise, separators and terminators change to Other Neutral. 8584 $prevlevel = -1; 8585 $levcount = 0; 8586 for ($i=0; $i < $numchars; ++$i) { 8587 if (($chardata[$i]['type'] == 'ET') OR ($chardata[$i]['type'] == 'ES') OR ($chardata[$i]['type'] == 'CS')) { 8588 $chardata[$i]['type'] = 'ON'; 8589 } 8590 if ($chardata[$i]['level'] != $prevlevel) { 8591 $levcount = 0; 8592 } else { 8593 ++$levcount; 8594 } 8595 $prevlevel = $chardata[$i]['level']; 8596 } 8597 8598 //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. 8599 $prevlevel = -1; 8600 $levcount = 0; 8601 for ($i=0; $i < $numchars; ++$i) { 8602 if ($chardata[$i]['char'] == 'EN') { 8603 for ($j=$levcount; $j >= 0; $j--) { 8604 if ($chardata[$j]['type'] == 'L') { 8605 $chardata[$i]['type'] = 'L'; 8606 } elseif ($chardata[$j]['type'] == 'R') { 8607 break; 8608 } 8609 } 8610 } 8611 if ($chardata[$i]['level'] != $prevlevel) { 8612 $levcount = 0; 8613 } else { 8614 ++$levcount; 8615 } 8616 $prevlevel = $chardata[$i]['level']; 8617 } 8618 8619 // 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. 8620 $prevlevel = -1; 8621 $levcount = 0; 8622 for ($i=0; $i < $numchars; ++$i) { 8623 if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) { 8624 if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) { 8625 $chardata[$i]['type'] = 'L'; 8626 } elseif (($chardata[$i]['type'] == 'N') AND 8627 (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND 8628 (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) { 8629 $chardata[$i]['type'] = 'R'; 8630 } elseif ($chardata[$i]['type'] == 'N') { 8631 // N2. Any remaining neutrals take the embedding direction 8632 $chardata[$i]['type'] = $chardata[$i]['sor']; 8633 } 8634 } elseif (($levcount == 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) { 8635 // first char 8636 if (($chardata[$i]['type'] == 'N') AND ($chardata[$i]['sor'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) { 8637 $chardata[$i]['type'] = 'L'; 8638 } elseif (($chardata[$i]['type'] == 'N') AND 8639 (($chardata[$i]['sor'] == 'R') OR ($chardata[$i]['sor'] == 'EN') OR ($chardata[$i]['sor'] == 'AN')) AND 8640 (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) { 8641 $chardata[$i]['type'] = 'R'; 8642 } elseif ($chardata[$i]['type'] == 'N') { 8643 // N2. Any remaining neutrals take the embedding direction 8644 $chardata[$i]['type'] = $chardata[$i]['sor']; 8645 } 8646 } elseif (($levcount > 0) AND ((($i+1) == $numchars) OR (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] != $prevlevel))) { 8647 //last char 8648 if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[$i]['eor'] == 'L')) { 8649 $chardata[$i]['type'] = 'L'; 8650 } elseif (($chardata[$i]['type'] == 'N') AND 8651 (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND 8652 (($chardata[$i]['eor'] == 'R') OR ($chardata[$i]['eor'] == 'EN') OR ($chardata[$i]['eor'] == 'AN'))) { 8653 $chardata[$i]['type'] = 'R'; 8654 } elseif ($chardata[$i]['type'] == 'N') { 8655 // N2. Any remaining neutrals take the embedding direction 8656 $chardata[$i]['type'] = $chardata[$i]['sor']; 8657 } 8658 } elseif ($chardata[$i]['type'] == 'N') { 8659 // N2. Any remaining neutrals take the embedding direction 8660 $chardata[$i]['type'] = $chardata[$i]['sor']; 8661 } 8662 if ($chardata[$i]['level'] != $prevlevel) { 8663 $levcount = 0; 8664 } else { 8665 ++$levcount; 8666 } 8667 $prevlevel = $chardata[$i]['level']; 8668 } 8669 8670 // 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. 8671 // I2. For all characters with an odd (right-to-left) embedding direction, those of type L, EN or AN go up one level. 8672 for ($i=0; $i < $numchars; ++$i) { 8673 $odd = $chardata[$i]['level'] % 2; 8674 if ($odd) { 8675 if (($chardata[$i]['type'] == 'L') OR ($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')) { 8676 $chardata[$i]['level'] += 1; 8677 } 8678 } else { 8679 if ($chardata[$i]['type'] == 'R') { 8680 $chardata[$i]['level'] += 1; 8681 } elseif (($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')) { 8682 $chardata[$i]['level'] += 2; 8683 } 8684 } 8685 $maxlevel = max($chardata[$i]['level'],$maxlevel); 8686 } 8687 8688 // L1. On each line, reset the embedding level of the following characters to the paragraph embedding level: 8689 // 1. Segment separators, 8690 // 2. Paragraph separators, 8691 // 3. Any sequence of whitespace characters preceding a segment separator or paragraph separator, and 8692 // 4. Any sequence of white space characters at the end of the line. 8693 for ($i=0; $i < $numchars; ++$i) { 8694 if (($chardata[$i]['type'] == 'B') OR ($chardata[$i]['type'] == 'S')) { 8695 $chardata[$i]['level'] = $pel; 8696 } elseif ($chardata[$i]['type'] == 'WS') { 8697 $j = $i+1; 8698 while ($j < $numchars) { 8699 if ((($chardata[$j]['type'] == 'B') OR ($chardata[$j]['type'] == 'S')) OR 8700 (($j == ($numchars-1)) AND ($chardata[$j]['type'] == 'WS'))) { 8701 $chardata[$i]['level'] = $pel; 8702 break; 8703 } elseif ($chardata[$j]['type'] != 'WS') { 8704 break; 8705 } 8706 ++$j; 8707 } 8708 } 8709 } 8710 8711 // Arabic Shaping 8712 // 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. 8713 if ($arabic) { 8714 $endedletter = array(1569,1570,1571,1572,1573,1575,1577,1583,1584,1585,1586,1608,1688); 8715 $alfletter = array(1570,1571,1573,1575); 8716 $chardata2 = $chardata; 8717 $laaletter = false; 8718 $charAL = array(); 8719 $x = 0; 8720 for ($i=0; $i < $numchars; ++$i) { 8721 if (($unicode[$chardata[$i]['char']] == 'AL') OR ($chardata[$i]['char'] == 32) OR ($chardata[$i]['char'] == 8204)) { 8722 $charAL[$x] = $chardata[$i]; 8723 $charAL[$x]['i'] = $i; 8724 $chardata[$i]['x'] = $x; 8725 ++$x; 8726 } 8727 } 8728 $numAL = $x; 8729 for ($i=0; $i < $numchars; ++$i) { 8730 $thischar = $chardata[$i]; 8731 if ($i > 0) { 8732 $prevchar = $chardata[($i-1)]; 8733 } else { 8734 $prevchar = false; 8735 } 8736 if (($i+1) < $numchars) { 8737 $nextchar = $chardata[($i+1)]; 8738 } else { 8739 $nextchar = false; 8740 } 8741 if ($unicode[$thischar['char']] == 'AL') { 8742 $x = $thischar['x']; 8743 if ($x > 0) { 8744 $prevchar = $charAL[($x-1)]; 8745 } else { 8746 $prevchar = false; 8747 } 8748 if (($x+1) < $numAL) { 8749 $nextchar = $charAL[($x+1)]; 8750 } else { 8751 $nextchar = false; 8752 } 8753 // if laa letter 8754 if (($prevchar !== false) AND ($prevchar['char'] == 1604) AND (in_array($thischar['char'], $alfletter))) { 8755 $arabicarr = $laa_array; 8756 $laaletter = true; 8757 if ($x > 1) { 8758 $prevchar = $charAL[($x-2)]; 8759 } else { 8760 $prevchar = false; 8761 } 8762 } else { 8763 $arabicarr = $unicode_arlet; 8764 $laaletter = false; 8765 } 8766 if (($prevchar !== false) AND ($nextchar !== false) AND 8767 (($unicode[$prevchar['char']] == 'AL') OR ($unicode[$prevchar['char']] == 'NSM')) AND 8768 (($unicode[$nextchar['char']] == 'AL') OR ($unicode[$nextchar['char']] == 'NSM')) AND 8769 ($prevchar['type'] == $thischar['type']) AND 8770 ($nextchar['type'] == $thischar['type']) AND 8771 ($nextchar['char'] != 1567)) { 8772 if (in_array($prevchar['char'], $endedletter)) { 8773 if (isset($arabicarr[$thischar['char']][2])) { 8774 // initial 8775 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2]; 8776 } 8777 } else { 8778 if (isset($arabicarr[$thischar['char']][3])) { 8779 // medial 8780 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][3]; 8781 } 8782 } 8783 } elseif (($nextchar !== false) AND 8784 (($unicode[$nextchar['char']] == 'AL') OR ($unicode[$nextchar['char']] == 'NSM')) AND 8785 ($nextchar['type'] == $thischar['type']) AND 8786 ($nextchar['char'] != 1567)) { 8787 if (isset($arabicarr[$chardata[$i]['char']][2])) { 8788 // initial 8789 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2]; 8790 } 8791 } elseif ((($prevchar !== false) AND 8792 (($unicode[$prevchar['char']] == 'AL') OR ($unicode[$prevchar['char']] == 'NSM')) AND 8793 ($prevchar['type'] == $thischar['type'])) OR 8794 (($nextchar !== false) AND ($nextchar['char'] == 1567))) { 8795 // final 8796 if (($i > 1) AND ($thischar['char'] == 1607) AND 8797 ($chardata[$i-1]['char'] == 1604) AND 8798 ($chardata[$i-2]['char'] == 1604)) { 8799 //Allah Word 8800 // mark characters to delete with false 8801 $chardata2[$i-2]['char'] = false; 8802 $chardata2[$i-1]['char'] = false; 8803 $chardata2[$i]['char'] = 65010; 8804 } else { 8805 if (($prevchar !== false) AND in_array($prevchar['char'], $endedletter)) { 8806 if (isset($arabicarr[$thischar['char']][0])) { 8807 // isolated 8808 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0]; 8809 } 8810 } else { 8811 if (isset($arabicarr[$thischar['char']][1])) { 8812 // final 8813 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][1]; 8814 } 8815 } 8816 } 8817 } elseif (isset($arabicarr[$thischar['char']][0])) { 8818 // isolated 8819 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0]; 8820 } 8821 // if laa letter 8822 if ($laaletter) { 8823 // mark characters to delete with false 8824 $chardata2[($charAL[($x-1)]['i'])]['char'] = false; 8825 } 8826 } // end if AL (Arabic Letter) 8827 } // end for each char 8828 /* 8829 * Combining characters that can occur with Shadda (0651 HEX, 1617 DEC) are placed in UE586-UE594. 8830 * Putting the combining mark and shadda in the same glyph allows us to avoid the two marks overlapping each other in an illegible manner. 8831 */ 8832 $cw = &$this->CurrentFont['cw']; 8833 for ($i = 0; $i < ($numchars-1); ++$i) { 8834 if (($chardata2[$i]['char'] == 1617) AND (isset($diacritics[($chardata2[$i+1]['char'])]))) { 8835 // check if the subtitution font is defined on current font 8836 if (isset($cw[($diacritics[($chardata2[$i+1]['char'])])])) { 8837 $chardata2[$i]['char'] = false; 8838 $chardata2[$i+1]['char'] = $diacritics[($chardata2[$i+1]['char'])]; 8839 } 8840 } 8841 } 8842 // remove marked characters 8843 foreach ($chardata2 as $key => $value) { 8844 if ($value['char'] === false) { 8845 unset($chardata2[$key]); 8846 } 8847 } 8848 $chardata = array_values($chardata2); 8849 $numchars = count($chardata); 8850 unset($chardata2); 8851 unset($arabicarr); 8852 unset($laaletter); 8853 unset($charAL); 8854 } 8855 8856 // 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. 8857 for ($j=$maxlevel; $j > 0; $j--) { 8858 $ordarray = Array(); 8859 $revarr = Array(); 8860 $onlevel = false; 8861 for ($i=0; $i < $numchars; ++$i) { 8862 if ($chardata[$i]['level'] >= $j) { 8863 $onlevel = true; 8864 if (isset($unicode_mirror[$chardata[$i]['char']])) { 8865 // 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. 8866 $chardata[$i]['char'] = $unicode_mirror[$chardata[$i]['char']]; 8867 } 8868 $revarr[] = $chardata[$i]; 8869 } else { 8870 if ($onlevel) { 8871 $revarr = array_reverse($revarr); 8872 $ordarray = array_merge($ordarray, $revarr); 8873 $revarr = Array(); 8874 $onlevel = false; 8875 } 8876 $ordarray[] = $chardata[$i]; 8877 } 8878 } 8879 if ($onlevel) { 8880 $revarr = array_reverse($revarr); 8881 $ordarray = array_merge($ordarray, $revarr); 8882 } 8883 $chardata = $ordarray; 8884 } 8885 8886 $ordarray = array(); 8887 for ($i=0; $i < $numchars; ++$i) { 8888 $ordarray[] = $chardata[$i]['char']; 8889 } 8890 8891 return $ordarray; 8892 } 8893 8894 // END OF BIDIRECTIONAL TEXT SECTION ------------------- 8895 8896 /* 8897 * Adds a bookmark. 8898 * @param string $txt bookmark description. 8899 * @param int $level bookmark level (minimum value is 0). 8900 * @param float $y Ordinate of the boorkmark position (default = -1 = current position). 8901 * @param int $page target page number (leave empty for current page). 8902 * @access public 8903 * @author Olivier Plathey, Nicola Asuni 8904 * @since 2.1.002 (2008-02-12) 8905 */ 8906 public function Bookmark($txt, $level=0, $y=-1, $page='') { 8907 if ($level < 0) { 8908 $level = 0; 8909 } 8910 if (isset($this->outlines[0])) { 8911 $lastoutline = end($this->outlines); 8912 $maxlevel = $lastoutline['l'] + 1; 8913 } else { 8914 $maxlevel = 0; 8915 } 8916 if ($level > $maxlevel) { 8917 $level = $maxlevel; 8918 } 8919 if ($y == -1) { 8920 $y = $this->GetY(); 8921 } 8922 if (empty($page)) { 8923 $page = $this->PageNo(); 8924 } 8925 $this->outlines[] = array('t' => $txt, 'l' => $level, 'y' => $y, 'p' => $page); 8926 } 8927 8928 /* 8929 * Create a bookmark PDF string. 8930 * @access protected 8931 * @author Olivier Plathey, Nicola Asuni 8932 * @since 2.1.002 (2008-02-12) 8933 */ 8934 protected function _putbookmarks() { 8935 $nb = count($this->outlines); 8936 if ($nb == 0) { 8937 return; 8938 } 8939 $lru = array(); 8940 $level = 0; 8941 foreach ($this->outlines as $i => $o) { 8942 if ($o['l'] > 0) { 8943 $parent = $lru[($o['l'] - 1)]; 8944 //Set parent and last pointers 8945 $this->outlines[$i]['parent'] = $parent; 8946 $this->outlines[$parent]['last'] = $i; 8947 if ($o['l'] > $level) { 8948 //Level increasing: set first pointer 8949 $this->outlines[$parent]['first'] = $i; 8950 } 8951 } else { 8952 $this->outlines[$i]['parent'] = $nb; 8953 } 8954 if (($o['l'] <= $level) AND ($i > 0)) { 8955 //Set prev and next pointers 8956 $prev = $lru[$o['l']]; 8957 $this->outlines[$prev]['next'] = $i; 8958 $this->outlines[$i]['prev'] = $prev; 8959 } 8960 $lru[$o['l']] = $i; 8961 $level = $o['l']; 8962 } 8963 //Outline items 8964 $n = $this->n + 1; 8965 foreach ($this->outlines as $i => $o) { 8966 $this->_newobj(); 8967 $this->_out('<</Title '.$this->_textstring($o['t'])); 8968 $this->_out('/Parent '.($n + $o['parent']).' 0 R'); 8969 if (isset($o['prev'])) 8970 $this->_out('/Prev '.($n + $o['prev']).' 0 R'); 8971 if (isset($o['next'])) 8972 $this->_out('/Next '.($n + $o['next']).' 0 R'); 8973 if (isset($o['first'])) 8974 $this->_out('/First '.($n + $o['first']).' 0 R'); 8975 if (isset($o['last'])) 8976 $this->_out('/Last '.($n + $o['last']).' 0 R'); 8977 $this->_out(sprintf('/Dest [%d 0 R /XYZ 0 %.2F null]', (1 + (2 * $o['p'])), ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k)))); 8978 $this->_out('/Count 0>>'); 8979 $this->_out('endobj'); 8980 } 8981 //Outline root 8982 $this->_newobj(); 8983 $this->OutlineRoot = $this->n; 8984 $this->_out('<</Type /Outlines /First '.$n.' 0 R'); 8985 $this->_out('/Last '.($n + $lru[0]).' 0 R>>'); 8986 $this->_out('endobj'); 8987 } 8988 8989 8990 // --- JAVASCRIPT - FORMS ------------------------------ 8991 8992 /* 8993 * Adds a javascript 8994 * @access public 8995 * @author Johannes G�ntert, Nicola Asuni 8996 * @since 2.1.002 (2008-02-12) 8997 */ 8998 public function IncludeJS($script) { 8999 $this->javascript .= $script; 9000 } 9001 9002 /* 9003 * Create a javascript PDF string. 9004 * @access protected 9005 * @author Johannes G�ntert, Nicola Asuni 9006 * @since 2.1.002 (2008-02-12) 9007 */ 9008 protected function _putjavascript() { 9009 if (empty($this->javascript)) { 9010 return; 9011 } 9012 // the following two lines are uded to avoid form fields duplication after saving 9013 $js1 = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%.2F,%.2F,%.2F,%.2F]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1); 9014 $js2 = "getField('tcpdfdocsaved').value = 'saved';"; 9015 $this->_newobj(); 9016 $this->n_js = $this->n; 9017 $this->_out('<<'); 9018 $this->_out('/Names [(EmbeddedJS) '.($this->n + 1).' 0 R ]'); 9019 $this->_out('>>'); 9020 $this->_out('endobj'); 9021 $this->_newobj(); 9022 $this->_out('<<'); 9023 $this->_out('/S /JavaScript'); 9024 $this->_out('/JS '.$this->_textstring($js1."\n".$this->javascript."\n".$js2)); 9025 $this->_out('>>'); 9026 $this->_out('endobj'); 9027 } 9028 9029 /* 9030 * Convert color to javascript color. 9031 * @param string $color color name or #RRGGBB 9032 * @access protected 9033 * @author Denis Van Nuffelen, Nicola Asuni 9034 * @since 2.1.002 (2008-02-12) 9035 */ 9036 protected function _JScolor($color) { 9037 static $aColors = array('transparent', 'black', 'white', 'red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'dkGray', 'gray', 'ltGray'); 9038 if (substr($color,0,1) == '#') { 9039 return sprintf("['RGB',%.3F,%.3F,%.3F]", hexdec(substr($color,1,2))/255, hexdec(substr($color,3,2))/255, hexdec(substr($color,5,2))/255); 9040 } 9041 if (!in_array($color,$aColors)) { 9042 $this->Error('Invalid color: '.$color); 9043 } 9044 return 'color.'.$color; 9045 } 9046 9047 /* 9048 * Adds a javascript form field. 9049 * @param string $type field type 9050 * @param string $name field name 9051 * @param int $x horizontal position 9052 * @param int $y vertical position 9053 * @param int $w width 9054 * @param int $h height 9055 * @param array $prop array of properties. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul> 9056 * @access protected 9057 * @author Denis Van Nuffelen, Nicola Asuni 9058 * @since 2.1.002 (2008-02-12) 9059 */ 9060 protected function _addfield($type, $name, $x, $y, $w, $h, $prop) { 9061 if ($this->rtl) { 9062 $x = $x - $w; 9063 } 9064 // the followind avoid fields duplication after saving the document 9065 $this->javascript .= "if(getField('tcpdfdocsaved').value != 'saved') {"; 9066 $k = $this->k; 9067 $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"; 9068 $this->javascript .= 'f'.$name.'.textSize='.$this->FontSizePt.";\n"; 9069 while (list($key, $val) = each($prop)) { 9070 if (strcmp(substr($key, -5), 'Color') == 0) { 9071 $val = $this->_JScolor($val); 9072 } else { 9073 $val = "'".$val."'"; 9074 } 9075 $this->javascript .= 'f'.$name.'.'.$key.'='.$val.";\n"; 9076 } 9077 if ($this->rtl) { 9078 $this->x -= $w; 9079 } else { 9080 $this->x += $w; 9081 } 9082 $this->javascript .= '}'; 9083 } 9084 9085 /* 9086 * Creates a text field 9087 * @param string $name field name 9088 * @param int $w width 9089 * @param int $h height 9090 * @param string $prop properties. The value property allows to set the initial value. The multiline property allows to define the field as multiline. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul> 9091 * @access public 9092 * @author Denis Van Nuffelen, Nicola Asuni 9093 * @since 2.1.002 (2008-02-12) 9094 */ 9095 public function TextField($name, $w, $h, $prop=array()) { 9096 $this->_addfield('text', $name, $this->x, $this->y, $w, $h, $prop); 9097 } 9098 9099 /* 9100 * Creates a RadioButton field 9101 * @param string $name field name 9102 * @param int $w width 9103 * @param string $prop properties. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul> 9104 * @access public 9105 * @author Nicola Asuni 9106 * @since 2.2.003 (2008-03-03) 9107 */ 9108 public function RadioButton($name, $w, $prop=array()) { 9109 if (!isset($prop['strokeColor'])) { 9110 $prop['strokeColor']='black'; 9111 } 9112 $this->_addfield('radiobutton', $name, $this->x, $this->y, $w, $w, $prop); 9113 } 9114 9115 /* 9116 * Creates a List-box field 9117 * @param string $name field name 9118 * @param int $w width 9119 * @param int $h height 9120 * @param array $values array containing the list of values. 9121 * @param string $prop properties. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul> 9122 * @access public 9123 * @author Nicola Asuni 9124 * @since 2.2.003 (2008-03-03) 9125 */ 9126 public function ListBox($name, $w, $h, $values, $prop=array()) { 9127 if (!isset($prop['strokeColor'])) { 9128 $prop['strokeColor'] = 'ltGray'; 9129 } 9130 $this->_addfield('listbox', $name, $this->x, $this->y, $w, $h, $prop); 9131 $s = ''; 9132 foreach ($values as $value) { 9133 $s .= "'".addslashes($value)."',"; 9134 } 9135 $this->javascript .= 'f'.$name.'.setItems(['.substr($s, 0, -1)."]);\n"; 9136 } 9137 9138 /* 9139 * Creates a Combo-box field 9140 * @param string $name field name 9141 * @param int $w width 9142 * @param int $h height 9143 * @param array $values array containing the list of values. 9144 * @param string $prop properties. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul> 9145 * @access public 9146 * @author Denis Van Nuffelen, Nicola Asuni 9147 * @since 2.1.002 (2008-02-12) 9148 */ 9149 public function ComboBox($name, $w, $h, $values, $prop=array()) { 9150 $this->_addfield('combobox', $name, $this->x, $this->y, $w, $h, $prop); 9151 $s = ''; 9152 foreach ($values as $value) { 9153 $s .= "'".addslashes($value)."',"; 9154 } 9155 $this->javascript .= 'f'.$name.'.setItems(['.substr($s, 0, -1)."]);\n"; 9156 } 9157 9158 /* 9159 * Creates a CheckBox field 9160 * @param string $name field name 9161 * @param int $w width 9162 * @param boolean $checked define the initial state (default = false). 9163 * @param string $prop properties. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul> 9164 * @access public 9165 * @author Denis Van Nuffelen, Nicola Asuni 9166 * @since 2.1.002 (2008-02-12) 9167 */ 9168 public function CheckBox($name, $w, $checked=false, $prop=array()) { 9169 $prop['value'] = ($checked ? 'Yes' : 'Off'); 9170 if (!isset($prop['strokeColor'])) { 9171 $prop['strokeColor'] = 'black'; 9172 } 9173 $this->_addfield('checkbox', $name, $this->x, $this->y, $w, $w, $prop); 9174 } 9175 9176 /* 9177 * Creates a button field 9178 * @param string $name field name 9179 * @param int $w width 9180 * @param int $h height 9181 * @param string $caption caption. 9182 * @param string $action action triggered by the button (JavaScript code). 9183 * @param string $prop properties. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul> 9184 * @access public 9185 * @author Denis Van Nuffelen, Nicola Asuni 9186 * @since 2.1.002 (2008-02-12) 9187 */ 9188 public function Button($name, $w, $h, $caption, $action, $prop=array()) { 9189 if (!isset($prop['strokeColor'])) { 9190 $prop['strokeColor'] = 'black'; 9191 } 9192 if (!isset($prop['borderStyle'])) { 9193 $prop['borderStyle'] = 'beveled'; 9194 } 9195 $this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop); 9196 $this->javascript .= 'f'.$name.".buttonSetCaption('".addslashes($caption)."');\n"; 9197 $this->javascript .= 'f'.$name.".setAction('MouseUp','".addslashes($action)."');\n"; 9198 $this->javascript .= 'f'.$name.".highlight='push';\n"; 9199 $this->javascript .= 'f'.$name.".print=false;\n"; 9200 } 9201 9202 // END JAVASCRIPT - FORMS ------------------------------ 9203 9204 /* 9205 * Enable Write permissions for PDF Reader. 9206 * WARNING: This works only using the Adobe private key with the setSignature() method. 9207 * @access protected 9208 * @author Nicola Asuni 9209 * @since 2.9.000 (2008-03-26) 9210 */ 9211 protected function _putuserrights() { 9212 if ((!$this->sign) OR (isset($this->signature_data['cert_type']) AND ($this->signature_data['cert_type'] > 0))) { 9213 return; 9214 } 9215 $this->_out('/Perms'); 9216 $this->_out('<<'); 9217 $this->_out('/UR3'); 9218 $this->_out('<<'); 9219 $this->_out('/Type/Sig'); 9220 $this->_out('/Filter/Adobe.PPKLite'); 9221 $this->_out('/SubFilter/adbe.pkcs7.detached'); 9222 $this->_out('/ByteRange[0 ********** ********** **********]'); 9223 $this->_out('/Contents<>'.str_repeat(' ', $this->signature_max_lenght)); 9224 if ($this->ur) { 9225 $this->_out('/Reference'); 9226 $this->_out('['); 9227 $this->_out('<<'); 9228 $this->_out('/Type/SigRef'); 9229 $this->_out('/TransformMethod/UR3'); 9230 $this->_out('/TransformParams'); 9231 $this->_out('<<'); 9232 $this->_out('/Type/TransformParams'); 9233 $this->_out('/V/2.2'); 9234 if (!$this->empty_string($this->ur_document)) { 9235 $this->_out('/Document['.$this->ur_document.']'); 9236 } 9237 if (!$this->empty_string($this->ur_annots)) { 9238 $this->_out('/Annots['.$this->ur_annots.']'); 9239 } 9240 if (!$this->empty_string($this->ur_form)) { 9241 $this->_out('/Form['.$this->ur_form.']'); 9242 } 9243 if (!$this->empty_string($this->ur_signature)) { 9244 $this->_out('/Signature['.$this->ur_signature.']'); 9245 } 9246 $this->_out('>>'); 9247 $this->_out('>>'); 9248 $this->_out(']'); 9249 } 9250 $this->_out('/M '.$this->_datastring('D:'.date('YmdHisO'))); 9251 $this->_out('>>'); 9252 $this->_out('>>'); 9253 } 9254 9255 /* 9256 * Add certification signature (DocMDP) 9257 * @access protected 9258 * @author Nicola Asuni 9259 * @since 4.6.008 (2009-05-07) 9260 */ 9261 protected function _putcertification() { 9262 if ((!$this->sign) OR (isset($this->signature_data['cert_type']) AND ($this->signature_data['cert_type'] <= 0))) { 9263 return; 9264 } 9265 $this->_out('/Perms'); 9266 $this->_out('<<'); 9267 $this->_out('/DocMDP'); 9268 $this->_out('<<'); 9269 $this->_out('/Type/Sig'); 9270 $this->_out('/Filter/Adobe.PPKLite'); 9271 $this->_out('/SubFilter/adbe.pkcs7.detached'); 9272 $this->_out('/ByteRange[0 ********** ********** **********]'); 9273 $this->_out('/Contents<>'.str_repeat(' ', $this->signature_max_lenght)); 9274 $this->_out('/Reference'); 9275 $this->_out('['); 9276 $this->_out('<<'); 9277 $this->_out('/Type/SigRef'); 9278 $this->_out('/TransformMethod/DocMDP'); 9279 $this->_out('/TransformParams'); 9280 $this->_out('<<'); 9281 $this->_out('/Type/TransformParams'); 9282 $this->_out('/V/1.2'); 9283 $this->_out('/P '.$this->signature_data['cert_type'].''); 9284 $this->_out('>>'); 9285 $this->_out('>>'); 9286 $this->_out(']'); 9287 $this->_out('/M '.$this->_datastring('D:'.date('YmdHisO'))); 9288 if (isset($this->signature_data['info']['Name']) AND !$this->empty_string($this->signature_data['info']['Name'])) { 9289 $this->_out('/Name '.$this->_textstring($this->signature_data['info']['Name']).''); 9290 } 9291 if (isset($this->signature_data['info']['Location']) AND !$this->empty_string($this->signature_data['info']['Location'])) { 9292 $this->_out('/Location '.$this->_textstring($this->signature_data['info']['Location']).''); 9293 } 9294 if (isset($this->signature_data['info']['Reason']) AND !$this->empty_string($this->signature_data['info']['Reason'])) { 9295 $this->_out('/Reason '.$this->_textstring($this->signature_data['info']['Reason']).''); 9296 } 9297 if (isset($this->signature_data['info']['ContactInfo']) AND !$this->empty_string($this->signature_data['info']['ContactInfo'])) { 9298 $this->_out('/ContactInfo '.$this->_textstring($this->signature_data['info']['ContactInfo']).''); 9299 } 9300 $this->_out('>>'); 9301 $this->_out('>>'); 9302 } 9303 9304 /* 9305 * Set User's Rights for PDF Reader 9306 * WARNING: This works only using the Adobe private key with the setSignature() method!. 9307 * Check the PDF Reference 8.7.1 Transform Methods, 9308 * Table 8.105 Entries in the UR transform parameters dictionary 9309 * @param boolean $enable if true enable user's rights on PDF reader 9310 * @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. 9311 * @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. 9312 * @param string $form Names specifying additional form-field-related usage rights for the document. Valid names are: /Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate 9313 * @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. 9314 * @access public 9315 * @author Nicola Asuni 9316 * @since 2.9.000 (2008-03-26) 9317 */ 9318 public function setUserRights( 9319 $enable=true, 9320 $document='/FullSave', 9321 $annots='/Create/Delete/Modify/Copy/Import/Export', 9322 $form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate', 9323 $signature='/Modify') { 9324 $this->ur = $enable; 9325 $this->ur_document = $document; 9326 $this->ur_annots = $annots; 9327 $this->ur_form = $form; 9328 $this->ur_signature = $signature; 9329 } 9330 9331 /* 9332 * Enable document signature (requires the OpenSSL Library). 9333 * The digital signature improve document authenticity and integrity and allows o enable extra features on Acrobat Reader. 9334 * @param mixed $signing_cert signing certificate (string or filename prefixed with 'file://') 9335 * @param mixed $private_key private key (string or filename prefixed with 'file://') 9336 * @param string $private_key_password password 9337 * @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. 9338 * @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. 9339 * @parm array $info array of option information: Name, Location, Reason, ContactInfo. 9340 * @access public 9341 * @author Nicola Asuni 9342 * @since 4.6.005 (2009-04-24) 9343 */ 9344 public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array()) { 9345 // to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.pem -out tcpdf.pem 9346 $this->sign = true; 9347 $this->signature_data = array(); 9348 if (strlen($signing_cert) == 0) { 9349 $signing_cert = 'file://'.dirname(__FILE__).'/tcpdf.pem'; 9350 } 9351 if (strlen($private_key) == 0) { 9352 $private_key = $signing_cert; 9353 } 9354 $this->signature_data['signcert'] = $signing_cert; 9355 $this->signature_data['privkey'] = $private_key; 9356 $this->signature_data['password'] = $private_key_password; 9357 $this->signature_data['extracerts'] = $extracerts; 9358 $this->signature_data['cert_type'] = $cert_type; 9359 $this->signature_data['info'] = array(); 9360 } 9361 9362 /* 9363 * Create a new page group. 9364 * NOTE: call this function before calling AddPage() 9365 * @param int $page starting group page (leave empty for next page). 9366 * @access public 9367 * @since 3.0.000 (2008-03-27) 9368 */ 9369 public function startPageGroup($page='') { 9370 if (empty($page)) { 9371 $page = $this->page + 1; 9372 } 9373 $this->newpagegroup[$page] = true; 9374 } 9375 9376 /** 9377 * Defines an alias for the total number of pages. 9378 * It will be substituted as the document is closed. 9379 * @param string $alias The alias. 9380 * @access public 9381 * @since 1.4 9382 * @see getAliasNbPages(), PageNo(), Footer() 9383 */ 9384 public function AliasNbPages($alias='{nb}') { 9385 $this->AliasNbPages = $alias; 9386 } 9387 9388 /** 9389 * Returns the string alias used for the total number of pages. 9390 * If the current font is unicode type, the returned string is surrounded by additional curly braces. 9391 * @return string 9392 * @access public 9393 * @since 4.0.018 (2008-08-08) 9394 * @see AliasNbPages(), PageNo(), Footer() 9395 */ 9396 public function getAliasNbPages() { 9397 if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) { 9398 return '{'.$this->AliasNbPages.'}'; 9399 } 9400 return $this->AliasNbPages; 9401 } 9402 9403 /** 9404 * Defines an alias for the page number. 9405 * It will be substituted as the document is closed. 9406 * @param string $alias The alias. 9407 * @access public 9408 * @since 4.5.000 (2009-01-02) 9409 * @see getAliasNbPages(), PageNo(), Footer() 9410 */ 9411 public function AliasNumPage($alias='{pnb}') { 9412 //Define an alias for total number of pages 9413 $this->AliasNumPage = $alias; 9414 } 9415 9416 /** 9417 * Returns the string alias used for the page number. 9418 * If the current font is unicode type, the returned string is surrounded by additional curly braces. 9419 * @return string 9420 * @access public 9421 * @since 4.5.000 (2009-01-02) 9422 * @see AliasNbPages(), PageNo(), Footer() 9423 */ 9424 public function getAliasNumPage() { 9425 if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) { 9426 return '{'.$this->AliasNumPage.'}'; 9427 } 9428 return $this->AliasNumPage; 9429 } 9430 9431 /* 9432 * Return the current page in the group. 9433 * @return current page in the group 9434 * @access public 9435 * @since 3.0.000 (2008-03-27) 9436 */ 9437 public function getGroupPageNo() { 9438 return $this->pagegroups[$this->currpagegroup]; 9439 } 9440 9441 /** 9442 * Returns the current group page number formatted as a string. 9443 * @access public 9444 * @since 4.3.003 (2008-11-18) 9445 * @see PaneNo(), formatPageNumber() 9446 */ 9447 public function getGroupPageNoFormatted() { 9448 return $this->formatPageNumber($this->getGroupPageNo()); 9449 } 9450 9451 /* 9452 * Return the alias of the current page group 9453 * If the current font is unicode type, the returned string is surrounded by additional curly braces. 9454 * (will be replaced by the total number of pages in this group). 9455 * @return alias of the current page group 9456 * @access public 9457 * @since 3.0.000 (2008-03-27) 9458 */ 9459 public function getPageGroupAlias() { 9460 if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) { 9461 return '{'.$this->currpagegroup.'}'; 9462 } 9463 return $this->currpagegroup; 9464 } 9465 9466 /* 9467 * Return the alias for the page number on the current page group 9468 * If the current font is unicode type, the returned string is surrounded by additional curly braces. 9469 * (will be replaced by the total number of pages in this group). 9470 * @return alias of the current page group 9471 * @access public 9472 * @since 4.5.000 (2009-01-02) 9473 */ 9474 public function getPageNumGroupAlias() { 9475 if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) { 9476 return '{'.str_replace('{nb', '{pnb', $this->currpagegroup).'}'; 9477 } 9478 return str_replace('{nb', '{pnb', $this->currpagegroup); 9479 } 9480 9481 /** 9482 * Format the page numbers. 9483 * This method can be overriden for custom formats. 9484 * @param int $num page number 9485 * @access protected 9486 * @since 4.2.005 (2008-11-06) 9487 */ 9488 protected function formatPageNumber($num) { 9489 return number_format((float)$num, 0, '', '.'); 9490 } 9491 9492 /** 9493 * Format the page numbers on the Table Of Content. 9494 * This method can be overriden for custom formats. 9495 * @param int $num page number 9496 * @access protected 9497 * @since 4.5.001 (2009-01-04) 9498 * @see addTOC() 9499 */ 9500 protected function formatTOCPageNumber($num) { 9501 return number_format((float)$num, 0, '', '.'); 9502 } 9503 9504 /** 9505 * Returns the current page number formatted as a string. 9506 * @access public 9507 * @since 4.2.005 (2008-11-06) 9508 * @see PaneNo(), formatPageNumber() 9509 */ 9510 public function PageNoFormatted() { 9511 return $this->formatPageNumber($this->PageNo()); 9512 } 9513 9514 /* 9515 * Put visibility settings. 9516 * @access protected 9517 * @since 3.0.000 (2008-03-27) 9518 */ 9519 protected function _putocg() { 9520 $this->_newobj(); 9521 $this->n_ocg_print = $this->n; 9522 $this->_out('<</Type /OCG /Name '.$this->_textstring('print')); 9523 $this->_out('/Usage <</Print <</PrintState /ON>> /View <</ViewState /OFF>>>>>>'); 9524 $this->_out('endobj'); 9525 $this->_newobj(); 9526 $this->n_ocg_view=$this->n; 9527 $this->_out('<</Type /OCG /Name '.$this->_textstring('view')); 9528 $this->_out('/Usage <</Print <</PrintState /OFF>> /View <</ViewState /ON>>>>>>'); 9529 $this->_out('endobj'); 9530 } 9531 9532 /* 9533 * Set the visibility of the successive elements. 9534 * This can be useful, for instance, to put a background 9535 * image or color that will show on screen but won't print. 9536 * @param string $v visibility mode. Legal values are: all, print, screen. 9537 * @access public 9538 * @since 3.0.000 (2008-03-27) 9539 */ 9540 public function setVisibility($v) { 9541 if ($this->openMarkedContent) { 9542 // close existing open marked-content 9543 $this->_out('EMC'); 9544 $this->openMarkedContent = false; 9545 } 9546 switch($v) { 9547 case 'print': { 9548 $this->_out('/OC /OC1 BDC'); 9549 $this->openMarkedContent = true; 9550 break; 9551 } 9552 case 'screen': { 9553 $this->_out('/OC /OC2 BDC'); 9554 $this->openMarkedContent = true; 9555 break; 9556 } 9557 case 'all': { 9558 $this->_out(''); 9559 break; 9560 } 9561 default: { 9562 $this->Error('Incorrect visibility: '.$v); 9563 break; 9564 } 9565 } 9566 $this->visibility = $v; 9567 } 9568 9569 /* 9570 * Add transparency parameters to the current extgstate 9571 * @param array $params parameters 9572 * @return the number of extgstates 9573 * @access protected 9574 * @since 3.0.000 (2008-03-27) 9575 */ 9576 protected function addExtGState($parms) { 9577 $n = count($this->extgstates) + 1; 9578 $this->extgstates[$n]['parms'] = $parms; 9579 return $n; 9580 } 9581 9582 /* 9583 * Add an extgstate 9584 * @param array $gs extgstate 9585 * @access protected 9586 * @since 3.0.000 (2008-03-27) 9587 */ 9588 protected function setExtGState($gs) { 9589 $this->_out(sprintf('/GS%d gs', $gs)); 9590 } 9591 9592 /* 9593 * Put extgstates for object transparency 9594 * @param array $gs extgstate 9595 * @access protected 9596 * @since 3.0.000 (2008-03-27) 9597 */ 9598 protected function _putextgstates() { 9599 $ne = count($this->extgstates); 9600 for ($i = 1; $i <= $ne; ++$i) { 9601 $this->_newobj(); 9602 $this->extgstates[$i]['n'] = $this->n; 9603 $this->_out('<</Type /ExtGState'); 9604 foreach ($this->extgstates[$i]['parms'] as $k => $v) { 9605 $this->_out('/'.$k.' '.$v); 9606 } 9607 $this->_out('>>'); 9608 $this->_out('endobj'); 9609 } 9610 } 9611 9612 /* 9613 * Set alpha for stroking (CA) and non-stroking (ca) operations. 9614 * @param float $alpha real value from 0 (transparent) to 1 (opaque) 9615 * @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 9616 * @access public 9617 * @since 3.0.000 (2008-03-27) 9618 */ 9619 public function setAlpha($alpha, $bm='Normal') { 9620 $gs = $this->addExtGState(array('ca' => $alpha, 'CA' => $alpha, 'BM' => '/'.$bm)); 9621 $this->setExtGState($gs); 9622 } 9623 9624 /* 9625 * Set the default JPEG compression quality (1-100) 9626 * @param int $quality JPEG quality, integer between 1 and 100 9627 * @access public 9628 * @since 3.0.000 (2008-03-27) 9629 */ 9630 public function setJPEGQuality($quality) { 9631 if (($quality < 1) OR ($quality > 100)) { 9632 $quality = 75; 9633 } 9634 $this->jpeg_quality = intval($quality); 9635 } 9636 9637 /* 9638 * Set the default number of columns in a row for HTML tables. 9639 * @param int $cols number of columns 9640 * @access public 9641 * @since 3.0.014 (2008-06-04) 9642 */ 9643 public function setDefaultTableColumns($cols=4) { 9644 $this->default_table_columns = intval($cols); 9645 } 9646 9647 /* 9648 * Set the height of cell repect font height. 9649 * @param int $h cell proportion respect font height (typical value = 1.25). 9650 * @access public 9651 * @since 3.0.014 (2008-06-04) 9652 */ 9653 public function setCellHeightRatio($h) { 9654 $this->cell_height_ratio = $h; 9655 } 9656 9657 /* 9658 * return the height of cell repect font height. 9659 * @access public 9660 * @since 4.0.012 (2008-07-24) 9661 */ 9662 public function getCellHeightRatio() { 9663 return $this->cell_height_ratio; 9664 } 9665 9666 /* 9667 * Set the PDF version (check PDF reference for valid values). 9668 * Default value is 1.t 9669 * @access public 9670 * @since 3.1.000 (2008-06-09) 9671 */ 9672 public function setPDFVersion($version='1.7') { 9673 $this->PDFVersion = $version; 9674 } 9675 9676 /* 9677 * Set the viewer preferences dictionary controlling the way the document is to be presented on the screen or in print. 9678 * (see Section 8.1 of PDF reference, "Viewer Preferences"). 9679 * <ul> 9680 * <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> 9681 * <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> 9682 * <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> 9683 * <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> 9684 * <li>CenterWindow boolean (Optional) A flag specifying whether to position the document's window in the center of the screen. Default value: false.</li> 9685 * <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> 9686 * <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> 9687 * <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> 9688 * <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> 9689 * <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> 9690 * <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> 9691 * <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> 9692 * <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> 9693 * <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> 9694 * <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> 9695 * <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> 9696 * </ul> 9697 * @param array $preferences array of options. 9698 * @author Nicola Asuni 9699 * @access public 9700 * @since 3.1.000 (2008-06-09) 9701 */ 9702 public function setViewerPreferences($preferences) { 9703 $this->viewer_preferences = $preferences; 9704 } 9705 9706 /** 9707 * Paints a linear colour gradient. 9708 * @param float $x abscissa of the top left corner of the rectangle. 9709 * @param float $y ordinate of the top left corner of the rectangle. 9710 * @param float $w width of the rectangle. 9711 * @param float $h height of the rectangle. 9712 * @param array $col1 first color (RGB components). 9713 * @param array $col2 second color (RGB components). 9714 * @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). 9715 * @author Andreas W�rmser, Nicola Asuni 9716 * @since 3.1.000 (2008-06-09) 9717 * @access public 9718 */ 9719 public function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) { 9720 $this->Clip($x, $y, $w, $h); 9721 $this->Gradient(2, $col1, $col2, $coords); 9722 } 9723 9724 /** 9725 * Paints a radial colour gradient. 9726 * @param float $x abscissa of the top left corner of the rectangle. 9727 * @param float $y ordinate of the top left corner of the rectangle. 9728 * @param float $w width of the rectangle. 9729 * @param float $h height of the rectangle. 9730 * @param array $col1 first color (RGB components). 9731 * @param array $col2 second color (RGB components). 9732 * @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. 9733 * @author Andreas W�rmser, Nicola Asuni 9734 * @since 3.1.000 (2008-06-09) 9735 * @access public 9736 */ 9737 public function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) { 9738 $this->Clip($x, $y, $w, $h); 9739 $this->Gradient(3, $col1, $col2, $coords); 9740 } 9741 9742 /** 9743 * Paints a coons patch mesh. 9744 * @param float $x abscissa of the top left corner of the rectangle. 9745 * @param float $y ordinate of the top left corner of the rectangle. 9746 * @param float $w width of the rectangle. 9747 * @param float $h height of the rectangle. 9748 * @param array $col1 first color (lower left corner) (RGB components). 9749 * @param array $col2 second color (lower right corner) (RGB components). 9750 * @param array $col3 third color (upper right corner) (RGB components). 9751 * @param array $col4 fourth color (upper left corner) (RGB components). 9752 * @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> 9753 * @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 9754 * @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 9755 * @author Andreas W�rmser, Nicola Asuni 9756 * @since 3.1.000 (2008-06-09) 9757 * @access public 9758 */ 9759 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) { 9760 $this->Clip($x, $y, $w, $h); 9761 $n = count($this->gradients) + 1; 9762 $this->gradients[$n]['type'] = 6; //coons patch mesh 9763 //check the coords array if it is the simple array or the multi patch array 9764 if (!isset($coords[0]['f'])) { 9765 //simple array -> convert to multi patch array 9766 if (!isset($col1[1])) { 9767 $col1[1] = $col1[2] = $col1[0]; 9768 } 9769 if (!isset($col2[1])) { 9770 $col2[1] = $col2[2] = $col2[0]; 9771 } 9772 if (!isset($col3[1])) { 9773 $col3[1] = $col3[2] = $col3[0]; 9774 } 9775 if (!isset($col4[1])) { 9776 $col4[1] = $col4[2] = $col4[0]; 9777 } 9778 $patch_array[0]['f'] = 0; 9779 $patch_array[0]['points'] = $coords; 9780 $patch_array[0]['colors'][0]['r'] = $col1[0]; 9781 $patch_array[0]['colors'][0]['g'] = $col1[1]; 9782 $patch_array[0]['colors'][0]['b'] = $col1[2]; 9783 $patch_array[0]['colors'][1]['r'] = $col2[0]; 9784 $patch_array[0]['colors'][1]['g'] = $col2[1]; 9785 $patch_array[0]['colors'][1]['b'] = $col2[2]; 9786 $patch_array[0]['colors'][2]['r'] = $col3[0]; 9787 $patch_array[0]['colors'][2]['g'] = $col3[1]; 9788 $patch_array[0]['colors'][2]['b'] = $col3[2]; 9789 $patch_array[0]['colors'][3]['r'] = $col4[0]; 9790 $patch_array[0]['colors'][3]['g'] = $col4[1]; 9791 $patch_array[0]['colors'][3]['b'] = $col4[2]; 9792 } else { 9793 //multi patch array 9794 $patch_array = $coords; 9795 } 9796 $bpcd = 65535; //16 BitsPerCoordinate 9797 //build the data stream 9798 $this->gradients[$n]['stream'] = ''; 9799 $count_patch = count($patch_array); 9800 for ($i=0; $i < $count_patch; ++$i) { 9801 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit 9802 $count_points = count($patch_array[$i]['points']); 9803 for ($j=0; $j < $count_points; ++$j) { 9804 //each point as 16 bit 9805 $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j] - $coords_min) / ($coords_max - $coords_min)) * $bpcd; 9806 if ($patch_array[$i]['points'][$j] < 0) { 9807 $patch_array[$i]['points'][$j] = 0; 9808 } 9809 if ($patch_array[$i]['points'][$j] > $bpcd) { 9810 $patch_array[$i]['points'][$j] = $bpcd; 9811 } 9812 $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] / 256)); 9813 $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] % 256)); 9814 } 9815 $count_cols = count($patch_array[$i]['colors']); 9816 for ($j=0; $j < $count_cols; ++$j) { 9817 //each color component as 8 bit 9818 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']); 9819 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']); 9820 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']); 9821 } 9822 } 9823 //paint the gradient 9824 $this->_out('/Sh'.$n.' sh'); 9825 //restore previous Graphic State 9826 $this->_out('Q'); 9827 } 9828 9829 /** 9830 * Set a rectangular clipping area. 9831 * @param float $x abscissa of the top left corner of the rectangle (or top right corner for RTL mode). 9832 * @param float $y ordinate of the top left corner of the rectangle. 9833 * @param float $w width of the rectangle. 9834 * @param float $h height of the rectangle. 9835 * @author Andreas W�rmser, Nicola Asuni 9836 * @since 3.1.000 (2008-06-09) 9837 * @access protected 9838 */ 9839 protected function Clip($x, $y, $w, $h) { 9840 if ($this->rtl) { 9841 $x = $this->w - $x - $w; 9842 } 9843 //save current Graphic State 9844 $s = 'q'; 9845 //set clipping area 9846 $s .= sprintf(' %.2F %.2F %.2F %.2F re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k); 9847 //set up transformation matrix for gradient 9848 $s .= sprintf(' %.3F 0 0 %.3F %.3F %.3F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k); 9849 $this->_out($s); 9850 } 9851 9852 /** 9853 * Output gradient. 9854 * @param int $type type of gradient. 9855 * @param array $col1 first color (RGB components). 9856 * @param array $col2 second color (RGB components). 9857 * @param array $coords array of coordinates. 9858 * @author Andreas W�rmser, Nicola Asuni 9859 * @since 3.1.000 (2008-06-09) 9860 * @access protected 9861 */ 9862 protected function Gradient($type, $col1, $col2, $coords) { 9863 $n = count($this->gradients) + 1; 9864 $this->gradients[$n]['type'] = $type; 9865 if (!isset($col1[1])) { 9866 $col1[1]=$col1[2]=$col1[0]; 9867 } 9868 $this->gradients[$n]['col1'] = sprintf('%.3F %.3F %.3F', ($col1[0]/255), ($col1[1]/255), ($col1[2]/255)); 9869 if (!isset($col2[1])) { 9870 $col2[1] = $col2[2] = $col2[0]; 9871 } 9872 $this->gradients[$n]['col2'] = sprintf('%.3F %.3F %.3F', ($col2[0]/255), ($col2[1]/255), ($col2[2]/255)); 9873 $this->gradients[$n]['coords'] = $coords; 9874 //paint the gradient 9875 $this->_out('/Sh'.$n.' sh'); 9876 //restore previous Graphic State 9877 $this->_out('Q'); 9878 } 9879 9880 /** 9881 * Output shaders. 9882 * @author Andreas W�rmser, Nicola Asuni 9883 * @since 3.1.000 (2008-06-09) 9884 * @access protected 9885 */ 9886 function _putshaders() { 9887 foreach ($this->gradients as $id => $grad) { 9888 if (($grad['type'] == 2) OR ($grad['type'] == 3)) { 9889 $this->_newobj(); 9890 $this->_out('<<'); 9891 $this->_out('/FunctionType 2'); 9892 $this->_out('/Domain [0.0 1.0]'); 9893 $this->_out('/C0 ['.$grad['col1'].']'); 9894 $this->_out('/C1 ['.$grad['col2'].']'); 9895 $this->_out('/N 1'); 9896 $this->_out('>>'); 9897 $this->_out('endobj'); 9898 $f1 = $this->n; 9899 } 9900 $this->_newobj(); 9901 $this->_out('<<'); 9902 $this->_out('/ShadingType '.$grad['type']); 9903 $this->_out('/ColorSpace /DeviceRGB'); 9904 if ($grad['type'] == 2) { 9905 $this->_out(sprintf('/Coords [%.3F %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3])); 9906 $this->_out('/Function '.$f1.' 0 R'); 9907 $this->_out('/Extend [true true] '); 9908 $this->_out('>>'); 9909 } elseif ($grad['type'] == 3) { 9910 //x0, y0, r0, x1, y1, r1 9911 //at this this time radius of inner circle is 0 9912 $this->_out(sprintf('/Coords [%.3F %.3F 0 %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3], $grad['coords'][4])); 9913 $this->_out('/Function '.$f1.' 0 R'); 9914 $this->_out('/Extend [true true] '); 9915 $this->_out('>>'); 9916 } elseif ($grad['type'] == 6) { 9917 $this->_out('/BitsPerCoordinate 16'); 9918 $this->_out('/BitsPerComponent 8'); 9919 $this->_out('/Decode[0 1 0 1 0 1 0 1 0 1]'); 9920 $this->_out('/BitsPerFlag 8'); 9921 $this->_out('/Length '.strlen($grad['stream'])); 9922 $this->_out('>>'); 9923 $this->_putstream($grad['stream']); 9924 } 9925 $this->_out('endobj'); 9926 $this->gradients[$id]['id'] = $this->n; 9927 } 9928 } 9929 9930 /** 9931 * Output an arc 9932 * @author Maxime Delorme, Nicola Asuni 9933 * @since 3.1.000 (2008-06-09) 9934 * @access protected 9935 */ 9936 protected function _outarc($x1, $y1, $x2, $y2, $x3, $y3 ) { 9937 $h = $this->h; 9938 $this->_out(sprintf('%.2F %.2F %.2F %.2F %.2F %.2F c', $x1*$this->k, ($h-$y1)*$this->k, $x2*$this->k, ($h-$y2)*$this->k, $x3*$this->k, ($h-$y3)*$this->k)); 9939 } 9940 9941 /** 9942 * Draw the sector of a circle. 9943 * It can be used for instance to render pie charts. 9944 * @param float $xc abscissa of the center. 9945 * @param float $yc ordinate of the center. 9946 * @param float $r radius. 9947 * @param float $a start angle (in degrees). 9948 * @param float $b end angle (in degrees). 9949 * @param string $style: D, F, FD or DF (draw, fill, fill and draw). Default: FD. 9950 * @param float $cw: indicates whether to go clockwise (default: true). 9951 * @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. 9952 * @author Maxime Delorme, Nicola Asuni 9953 * @since 3.1.000 (2008-06-09) 9954 * @access public 9955 */ 9956 public function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) { 9957 if ($this->rtl) { 9958 $xc = $this->w - $xc; 9959 } 9960 if ($cw) { 9961 $d = $b; 9962 $b = $o - $a; 9963 $a = $o - $d; 9964 } else { 9965 $b += $o; 9966 $a += $o; 9967 } 9968 $a = ($a % 360) + 360; 9969 $b = ($b % 360) + 360; 9970 if ($a > $b) { 9971 $b +=360; 9972 } 9973 $b = $b / 360 * 2 * M_PI; 9974 $a = $a / 360 * 2 * M_PI; 9975 $d = $b - $a; 9976 if ($d == 0 ) { 9977 $d = 2 * M_PI; 9978 } 9979 $k = $this->k; 9980 $hp = $this->h; 9981 if ($style=='F') { 9982 $op = 'f'; 9983 } elseif ($style=='FD' or $style=='DF') { 9984 $op = 'b'; 9985 } else { 9986 $op = 's'; 9987 } 9988 if (sin($d/2)) { 9989 $MyArc = 4/3 * (1 - cos($d/2)) / sin($d/2) * $r; 9990 } 9991 //first put the center 9992 $this->_out(sprintf('%.2F %.2F m', ($xc)*$k, ($hp-$yc)*$k)); 9993 //put the first point 9994 $this->_out(sprintf('%.2F %.2F l', ($xc+$r*cos($a))*$k, (($hp-($yc-$r*sin($a)))*$k))); 9995 //draw the arc 9996 if ($d < (M_PI/2)) { 9997 $this->_outarc($xc+$r*cos($a)+$MyArc*cos(M_PI/2+$a), $yc-$r*sin($a)-$MyArc*sin(M_PI/2+$a), $xc+$r*cos($b)+$MyArc*cos($b-M_PI/2), $yc-$r*sin($b)-$MyArc*sin($b-M_PI/2), $xc+$r*cos($b), $yc-$r*sin($b)); 9998 } else { 9999 $b = $a + $d/4; 10000 $MyArc = 4/3*(1-cos($d/8))/sin($d/8)*$r; 10001 $this->_outarc($xc+$r*cos($a)+$MyArc*cos(M_PI/2+$a), $yc-$r*sin($a)-$MyArc*sin(M_PI/2+$a), $xc+$r*cos($b)+$MyArc*cos($b-M_PI/2), $yc-$r*sin($b)-$MyArc*sin($b-M_PI/2), $xc+$r*cos($b), $yc-$r*sin($b)); 10002 $a = $b; 10003 $b = $a + $d/4; 10004 $this->_outarc($xc+$r*cos($a)+$MyArc*cos(M_PI/2+$a), $yc-$r*sin($a)-$MyArc*sin(M_PI/2+$a), $xc+$r*cos($b)+$MyArc*cos($b-M_PI/2), $yc-$r*sin($b)-$MyArc*sin($b-M_PI/2), $xc+$r*cos($b), $yc-$r*sin($b)); 10005 $a = $b; 10006 $b = $a + $d/4; 10007 $this->_outarc($xc+$r*cos($a)+$MyArc*cos(M_PI/2+$a), $yc-$r*sin($a)-$MyArc*sin(M_PI/2+$a), $xc+$r*cos($b)+$MyArc*cos($b-M_PI/2), $yc-$r*sin($b)-$MyArc*sin($b-M_PI/2), $xc+$r*cos($b), $yc-$r*sin($b) ); 10008 $a = $b; 10009 $b = $a + $d/4; 10010 $this->_outarc($xc+$r*cos($a)+$MyArc*cos(M_PI/2+$a), $yc-$r*sin($a)-$MyArc*sin(M_PI/2+$a), $xc+$r*cos($b)+$MyArc*cos($b-M_PI/2), $yc-$r*sin($b)-$MyArc*sin($b-M_PI/2), $xc+$r*cos($b), $yc-$r*sin($b)); 10011 } 10012 //terminate drawing 10013 $this->_out($op); 10014 } 10015 10016 /** 10017 * Embed vector-based Adobe Illustrator (AI) or AI-compatible EPS files. 10018 * Only vector drawing is supported, not text or bitmap. 10019 * 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). 10020 * @param string $file Name of the file containing the image. 10021 * @param float $x Abscissa of the upper-left corner. 10022 * @param float $y Ordinate of the upper-left corner. 10023 * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated. 10024 * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated. 10025 * @param mixed $link URL or identifier returned by AddLink(). 10026 * @param boolean useBoundingBox specifies whether to position the bounding box (true) or the complete canvas (false) at location (x,y). Default value is true. 10027 * @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> 10028 * @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> 10029 * @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> 10030 * @author Valentin Schmidt, Nicola Asuni 10031 * @since 3.1.000 (2008-06-09) 10032 * @access public 10033 */ 10034 public function ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0) { 10035 if ($x === '') { 10036 $x = $this->x; 10037 } 10038 if ($y === '') { 10039 $y = $this->y; 10040 } 10041 $k = $this->k; 10042 $data = file_get_contents($file); 10043 if ($data === false) { 10044 $this->Error('EPS file not found: '.$file); 10045 } 10046 $regs = array(); 10047 // EPS/AI compatibility check (only checks files created by Adobe Illustrator!) 10048 preg_match("/%%Creator:([^\r\n]+)/", $data, $regs); # find Creator 10049 if (count($regs) > 1) { 10050 $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0" 10051 if (strpos($version_str, 'Adobe Illustrator') !== false) { 10052 $versexp = explode(' ', $version_str); 10053 $version = (float)array_pop($versexp); 10054 if ($version >= 9) { 10055 $this->Error('This version of Adobe Illustrator file is not supported: '.$file); 10056 } 10057 } 10058 } 10059 // strip binary bytes in front of PS-header 10060 $start = strpos($data, '%!PS-Adobe'); 10061 if ($start > 0) { 10062 $data = substr($data, $start); 10063 } 10064 // find BoundingBox params 10065 preg_match("/%%BoundingBox:([^\r\n]+)/", $data, $regs); 10066 if (count($regs) > 1) { 10067 list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1])); 10068 } else { 10069 $this->Error('No BoundingBox found in EPS file: '.$file); 10070 } 10071 $start = strpos($data, '%%EndSetup'); 10072 if ($start === false) { 10073 $start = strpos($data, '%%EndProlog'); 10074 } 10075 if ($start === false) { 10076 $start = strpos($data, '%%BoundingBox'); 10077 } 10078 $data = substr($data, $start); 10079 $end = strpos($data, '%%PageTrailer'); 10080 if ($end===false) { 10081 $end = strpos($data, 'showpage'); 10082 } 10083 if ($end) { 10084 $data = substr($data, 0, $end); 10085 } 10086 if ($w > 0) { 10087 $scale_x = $w / (($x2 - $x1) / $k); 10088 if ($h > 0) { 10089 $scale_y = $h / (($y2 - $y1) / $k); 10090 } else { 10091 $scale_y = $scale_x; 10092 $h = ($y2 - $y1) / $k * $scale_y; 10093 } 10094 } else { 10095 if ($h > 0) { 10096 $scale_y = $h / (($y2 - $y1) / $k); 10097 $scale_x = $scale_y; 10098 $w = ($x2-$x1) / $k * $scale_x; 10099 } else { 10100 $w = ($x2 - $x1) / $k; 10101 $h = ($y2 - $y1) / $k; 10102 } 10103 } 10104 // Check whether we need a new page first as this does not fit 10105 if ($this->checkPageBreak($h, $y)) { 10106 $y = $this->GetY() + $this->cMargin; 10107 } 10108 // set bottomcoordinates 10109 $this->img_rb_y = $y + $h; 10110 // set alignment 10111 if ($this->rtl) { 10112 if ($palign == 'L') { 10113 $ximg = $this->lMargin; 10114 // set right side coordinate 10115 $this->img_rb_x = $ximg + $w; 10116 } elseif ($palign == 'C') { 10117 $ximg = ($this->w - $x - $w) / 2; 10118 // set right side coordinate 10119 $this->img_rb_x = $ximg + $w; 10120 } else { 10121 $ximg = $this->w - $x - $w; 10122 // set left side coordinate 10123 $this->img_rb_x = $ximg; 10124 } 10125 } else { 10126 if ($palign == 'R') { 10127 $ximg = $this->w - $this->rMargin - $w; 10128 // set left side coordinate 10129 $this->img_rb_x = $ximg; 10130 } elseif ($palign == 'C') { 10131 $ximg = ($this->w - $x - $w) / 2; 10132 // set right side coordinate 10133 $this->img_rb_x = $ximg + $w; 10134 } else { 10135 $ximg = $x; 10136 // set right side coordinate 10137 $this->img_rb_x = $ximg + $w; 10138 } 10139 } 10140 if ($useBoundingBox) { 10141 $dx = $ximg * $k - $x1; 10142 $dy = $y * $k - $y1; 10143 } else { 10144 $dx = $ximg * $k; 10145 $dy = $y * $k; 10146 } 10147 // save the current graphic state 10148 $this->_out('q'.$this->epsmarker); 10149 // translate 10150 $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', 1, 0, 0, 1, $dx, $dy + ($this->hPt - (2 * $y * $k) - ($y2 - $y1)))); 10151 // scale 10152 if (isset($scale_x)) { 10153 $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $scale_x, 0, 0, $scale_y, $x1 * (1 - $scale_x), $y2 * (1 - $scale_y))); 10154 } 10155 // handle pc/unix/mac line endings 10156 preg_match('/[\r\n]+/s', $data, $regs); 10157 $lines = explode($regs[0], $data); 10158 $u=0; 10159 $cnt = count($lines); 10160 for ($i=0; $i < $cnt; ++$i) { 10161 $line = $lines[$i]; 10162 if (($line == '') OR ($line{0} == '%')) { 10163 continue; 10164 } 10165 $len = strlen($line); 10166 $chunks = explode(' ', $line); 10167 $cmd = array_pop($chunks); 10168 // RGB 10169 if (($cmd == 'Xa') OR ($cmd == 'XA')) { 10170 $b = array_pop($chunks); 10171 $g = array_pop($chunks); 10172 $r = array_pop($chunks); 10173 $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! 10174 continue; 10175 } 10176 switch ($cmd) { 10177 case 'm': 10178 case 'l': 10179 case 'v': 10180 case 'y': 10181 case 'c': 10182 case 'k': 10183 case 'K': 10184 case 'g': 10185 case 'G': 10186 case 's': 10187 case 'S': 10188 case 'J': 10189 case 'j': 10190 case 'w': 10191 case 'M': 10192 case 'd': 10193 case 'n': 10194 case 'v': { 10195 $this->_out($line); 10196 break; 10197 } 10198 case 'x': {// custom fill color 10199 list($c,$m,$y,$k) = $chunks; 10200 $this->_out(''.$c.' '.$m.' '.$y.' '.$k.' k'); 10201 break; 10202 } 10203 case 'X': { // custom stroke color 10204 list($c,$m,$y,$k) = $chunks; 10205 $this->_out(''.$c.' '.$m.' '.$y.' '.$k.' K'); 10206 break; 10207 } 10208 case 'Y': 10209 case 'N': 10210 case 'V': 10211 case 'L': 10212 case 'C': { 10213 $line{$len-1} = strtolower($cmd); 10214 $this->_out($line); 10215 break; 10216 } 10217 case 'b': 10218 case 'B': { 10219 $this->_out($cmd . '*'); 10220 break; 10221 } 10222 case 'f': 10223 case 'F': { 10224 if ($u > 0) { 10225 $isU = false; 10226 $max = min($i+5, $cnt); 10227 for ($j=$i+1; $j < $max; ++$j) 10228 $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U'))); 10229 if ($isU) { 10230 $this->_out('f*'); 10231 } 10232 } else { 10233 $this->_out('f*'); 10234 } 10235 break; 10236 } 10237 case '*u': { 10238 ++$u; 10239 break; 10240 } 10241 case '*U': { 10242 --$u; 10243 break; 10244 } 10245 } 10246 } 10247 // restore previous graphic state 10248 $this->_out($this->epsmarker.'Q'); 10249 if (!empty($border)) { 10250 $bx = $x; 10251 $by = $y; 10252 $this->x = $x; 10253 $this->y = $y; 10254 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0); 10255 $this->x = $bx; 10256 $this->y = $by; 10257 } 10258 if ($link) { 10259 $this->Link($ximg, $y, $w, $h, $link, 0); 10260 } 10261 // set pointer to align the successive text/objects 10262 switch($align) { 10263 case 'T':{ 10264 $this->y = $y; 10265 $this->x = $this->img_rb_x; 10266 break; 10267 } 10268 case 'M':{ 10269 $this->y = $y + round($h/2); 10270 $this->x = $this->img_rb_x; 10271 break; 10272 } 10273 case 'B':{ 10274 $this->y = $this->img_rb_y; 10275 $this->x = $this->img_rb_x; 10276 break; 10277 } 10278 case 'N':{ 10279 $this->SetY($this->img_rb_y); 10280 break; 10281 } 10282 default:{ 10283 break; 10284 } 10285 } 10286 $this->endlinex = $this->img_rb_x; 10287 } 10288 10289 /** 10290 * Set document barcode. 10291 * @param string $bc barcode 10292 * @access public 10293 */ 10294 public function setBarcode($bc='') { 10295 $this->barcode = $bc; 10296 } 10297 10298 /** 10299 * Get current barcode. 10300 * @return string 10301 * @access public 10302 * @since 4.0.012 (2008-07-24) 10303 */ 10304 public function getBarcode() { 10305 return $this->barcode; 10306 } 10307 10308 /** 10309 * Print a Linear Barcode. 10310 * @param string $code code to print 10311 * @param string $type type of barcode. 10312 * @param int $x x position in user units 10313 * @param int $y y position in user units 10314 * @param int $w width in user units 10315 * @param int $h height in user units 10316 * @param float $xres width of the smallest bar in user units 10317 * @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</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> 10318 * @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> 10319 * @author Nicola Asuni 10320 * @since 3.1.000 (2008-06-09) 10321 * @access public 10322 */ 10323 public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres=0.4, $style='', $align='') { 10324 if ($this->empty_string($code)) { 10325 return; 10326 } 10327 require_once(dirname(__FILE__).'/barcodes.php'); 10328 // save current graphic settings 10329 $gvars = $this->getGraphicVars(); 10330 // create new barcode object 10331 $barcodeobj = new TCPDFBarcode($code, $type); 10332 $arrcode = $barcodeobj->getBarcodeArray(); 10333 if ($arrcode === false) { 10334 $this->Error('Error in 1D barcode string'); 10335 } 10336 // set default values 10337 if (!isset($style['position'])) { 10338 if ($this->rtl) { 10339 $style['position'] = 'R'; 10340 } else { 10341 $style['position'] = 'L'; 10342 } 10343 } 10344 if (!isset($style['padding'])) { 10345 $style['padding'] = 0; 10346 } 10347 if (!isset($style['fgcolor'])) { 10348 $style['fgcolor'] = array(0,0,0); // default black 10349 } 10350 if (!isset($style['bgcolor'])) { 10351 $style['bgcolor'] = false; // default transparent 10352 } 10353 if (!isset($style['border'])) { 10354 $style['border'] = false; 10355 } 10356 if (!isset($style['text'])) { 10357 $style['text'] = false; 10358 $fontsize = 0; 10359 } 10360 if ($style['text'] AND isset($style['font'])) { 10361 if (isset($style['fontsize'])) { 10362 $fontsize = $style['fontsize']; 10363 } else { 10364 $fontsize = 0; 10365 } 10366 $this->SetFont($style['font'], '', $fontsize); 10367 } 10368 if (!isset($style['stretchtext'])) { 10369 $style['stretchtext'] = 4; 10370 } 10371 // set foreground color 10372 $this->SetDrawColorArray($style['fgcolor']); 10373 $this->SetTextColorArray($style['fgcolor']); 10374 if ($this->empty_string($w) OR ($w <= 0)) { 10375 if ($this->rtl) { 10376 $w = $this->x - $this->lMargin; 10377 } else { 10378 $w = $this->w - $this->rMargin - $this->x; 10379 } 10380 } 10381 if ($this->empty_string($x)) { 10382 $x = $this->GetX(); 10383 } 10384 if ($this->rtl) { 10385 $x = $this->w - $x; 10386 } 10387 if ($this->empty_string($y)) { 10388 $y = $this->GetY(); 10389 } 10390 if ($this->empty_string($xres)) { 10391 $xres = 0.4; 10392 } 10393 $fbw = ($arrcode['maxw'] * $xres) + (2 * $style['padding']); 10394 $extraspace = ($this->cell_height_ratio * $fontsize / $this->k) + (2 * $style['padding']); 10395 if ($this->empty_string($h) OR ($h <= 0)) { 10396 $h = 10 + $extraspace; 10397 } 10398 if ($this->checkPageBreak($h)) { 10399 $y = $this->y; 10400 } 10401 // maximum bar heigth 10402 $barh = $h - $extraspace; 10403 switch ($style['position']) { 10404 case 'L': { // left 10405 if ($this->rtl) { 10406 $xpos = $x - $w; 10407 } else { 10408 $xpos = $x; 10409 } 10410 break; 10411 } 10412 case 'C': { // center 10413 $xdiff = (($w - $fbw) / 2); 10414 if ($this->rtl) { 10415 $xpos = $x - $w + $xdiff; 10416 } else { 10417 $xpos = $x + $xdiff; 10418 } 10419 break; 10420 } 10421 case 'R': { // right 10422 if ($this->rtl) { 10423 $xpos = $x - $fbw; 10424 } else { 10425 $xpos = $x + $w - $fbw; 10426 } 10427 break; 10428 } 10429 case 'S': { // stretch 10430 $fbw = $w; 10431 $xres = ($w - (2 * $style['padding'])) / $arrcode['maxw']; 10432 if ($this->rtl) { 10433 $xpos = $x - $w; 10434 } else { 10435 $xpos = $x; 10436 } 10437 break; 10438 } 10439 } 10440 $xpos_rect = $xpos; 10441 $xpos = $xpos_rect + $style['padding']; 10442 $xpos_text = $xpos; 10443 // barcode is always printed in LTR direction 10444 $tempRTL = $this->rtl; 10445 $this->rtl = false; 10446 // print background color 10447 if ($style['bgcolor']) { 10448 $this->Rect($xpos_rect, $y, $fbw, $h, 'DF', '', $style['bgcolor']); 10449 } elseif ($style['border']) { 10450 $this->Rect($xpos_rect, $y, $fbw, $h, 'D'); 10451 } 10452 // print bars 10453 if ($arrcode !== false) { 10454 foreach ($arrcode['bcode'] as $k => $v) { 10455 $bw = ($v['w'] * $xres); 10456 if ($v['t']) { 10457 // draw a vertical bar 10458 $ypos = $y + $style['padding'] + ($v['p'] * $barh / $arrcode['maxh']); 10459 $this->Rect($xpos, $ypos, $bw, ($v['h'] * $barh / $arrcode['maxh']), 'F', array(), $style['fgcolor']); 10460 } 10461 $xpos += $bw; 10462 } 10463 } 10464 // print text 10465 if ($style['text']) { 10466 // print text 10467 $this->x = $xpos_text; 10468 $this->y = $y + $style['padding'] + $barh; 10469 $this->Cell(($arrcode['maxw'] * $xres), ($this->cell_height_ratio * $fontsize / $this->k), $code, 0, 0, 'C', 0, '', $style['stretchtext']); 10470 } 10471 // restore original direction 10472 $this->rtl = $tempRTL; 10473 // restore previous settings 10474 $this->setGraphicVars($gvars); 10475 // set bottomcoordinates 10476 $this->img_rb_y = $y + $h; 10477 if ($this->rtl) { 10478 // set left side coordinate 10479 $this->img_rb_x = ($this->w - $x - $w); 10480 } else { 10481 // set right side coordinate 10482 $this->img_rb_x = $x + $w; 10483 } 10484 // set pointer to align the successive text/objects 10485 switch($align) { 10486 case 'T':{ 10487 $this->y = $y; 10488 $this->x = $this->img_rb_x; 10489 break; 10490 } 10491 case 'M':{ 10492 $this->y = $y + round($h/2); 10493 $this->x = $this->img_rb_x; 10494 break; 10495 } 10496 case 'B':{ 10497 $this->y = $this->img_rb_y; 10498 $this->x = $this->img_rb_x; 10499 break; 10500 } 10501 case 'N':{ 10502 $this->SetY($this->img_rb_y); 10503 break; 10504 } 10505 default:{ 10506 break; 10507 } 10508 } 10509 } 10510 10511 /** 10512 * This function is DEPRECATED, please use the new write1DBarcode() function. 10513 * @param int $x x position in user units 10514 * @param int $y y position in user units 10515 * @param int $w width in user units 10516 * @param int $h height position in user units 10517 * @param string $type type of barcode (I25, C128A, C128B, C128C, C39) 10518 * @param string $style barcode style 10519 * @param string $font font for text 10520 * @param int $xres x resolution 10521 * @param string $code code to print 10522 * @deprecated deprecated since version 3.1.000 (2008-06-10) 10523 * @access public 10524 * @see write1DBarcode() 10525 */ 10526 public function writeBarcode($x, $y, $w, $h, $type, $style, $font, $xres, $code) { 10527 // convert old settings for the new write1DBarcode() function. 10528 $xres = 1 / $xres; 10529 $newstyle = array( 10530 'position' => 'L', 10531 'border' => false, 10532 'padding' => 0, 10533 'fgcolor' => array(0,0,0), 10534 'bgcolor' => false, 10535 'text' => true, 10536 'font' => $font, 10537 'fontsize' => 8, 10538 'stretchtext' => 4 10539 ); 10540 if ($style & 1) { 10541 $newstyle['border'] = true; 10542 } 10543 if ($style & 2) { 10544 $newstyle['bgcolor'] = false; 10545 } 10546 if ($style & 4) { 10547 $newstyle['position'] = 'C'; 10548 } elseif ($style & 8) { 10549 $newstyle['position'] = 'L'; 10550 } elseif ($style & 16) { 10551 $newstyle['position'] = 'R'; 10552 } 10553 if ($style & 128) { 10554 $newstyle['text'] = true; 10555 } 10556 if ($style & 256) { 10557 $newstyle['stretchtext'] = 4; 10558 } 10559 $this->write1DBarcode($code, $type, $x, $y, $w, $h, $xres, $newstyle, ''); 10560 } 10561 10562 /** 10563 * Print 2D Barcode. 10564 * @param string $code code to print 10565 * @param string $type type of barcode. 10566 * @param int $x x position in user units 10567 * @param int $y y position in user units 10568 * @param int $w width in user units 10569 * @param int $h height in user units 10570 * @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</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> 10571 * @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> 10572 * @author Nicola Asuni 10573 * @since 4.5.037 (2009-04-07) 10574 * @access public 10575 */ 10576 public function write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='') { 10577 if ($this->empty_string($code)) { 10578 return; 10579 } 10580 require_once(dirname(__FILE__).'/2dbarcodes.php'); 10581 // save current graphic settings 10582 $gvars = $this->getGraphicVars(); 10583 // create new barcode object 10584 $barcodeobj = new TCPDF2DBarcode($code, $type); 10585 $arrcode = $barcodeobj->getBarcodeArray(); 10586 if ($arrcode === false) { 10587 $this->Error('Error in 2D barcode string'); 10588 } 10589 // set default values 10590 if (!isset($style['padding'])) { 10591 $style['padding'] = 0; 10592 } 10593 if (!isset($style['fgcolor'])) { 10594 $style['fgcolor'] = array(0,0,0); // default black 10595 } 10596 if (!isset($style['bgcolor'])) { 10597 $style['bgcolor'] = false; // default transparent 10598 } 10599 if (!isset($style['border'])) { 10600 $style['border'] = false; 10601 } 10602 // set foreground color 10603 $this->SetDrawColorArray($style['fgcolor']); 10604 if ($this->empty_string($x)) { 10605 $x = $this->GetX(); 10606 } 10607 if ($this->rtl) { 10608 $x = $this->w - $x; 10609 } 10610 if ($this->empty_string($y)) { 10611 $y = $this->GetY(); 10612 } 10613 if ($this->empty_string($w) OR ($w <= 0)) { 10614 if ($this->rtl) { 10615 $w = $x - $this->lMargin; 10616 } else { 10617 $w = $this->w - $this->rMargin - $x; 10618 } 10619 } 10620 if ($this->empty_string($h) OR ($h <= 0)) { 10621 // 2d barcodes are square by default 10622 $h = $w; 10623 } 10624 if ($this->checkPageBreak($h)) { 10625 $y = $this->y; 10626 } 10627 // calculate barcode size (excluding padding) 10628 $bw = $w - (2 * $style['padding']); 10629 $bh = $h - (2 * $style['padding']); 10630 // calculate starting coordinates 10631 if ($this->rtl) { 10632 $xpos = $x - $w; 10633 } else { 10634 $xpos = $x; 10635 } 10636 $xpos += $style['padding']; 10637 $ypos = $y + $style['padding']; 10638 // barcode is always printed in LTR direction 10639 $tempRTL = $this->rtl; 10640 $this->rtl = false; 10641 // print background color 10642 if ($style['bgcolor']) { 10643 $this->Rect($x, $y, $w, $h, 'DF', '', $style['bgcolor']); 10644 } elseif ($style['border']) { 10645 $this->Rect($x, $y, $w, $h, 'D'); 10646 } 10647 // print barcode cells 10648 if ($arrcode !== false) { 10649 $rows = $arrcode['num_rows']; 10650 $cols = $arrcode['num_cols']; 10651 // calculate dimension of single barcode cell 10652 $cw = $bw / $cols; 10653 $ch = $bh / $rows; 10654 // for each row 10655 for ($r = 0; $r < $rows; ++$r) { 10656 $xr = $xpos; 10657 // for each column 10658 for ($c = 0; $c < $cols; ++$c) { 10659 if ($arrcode['bcode'][$r][$c] == 1) { 10660 // draw a single barcode cell 10661 $this->Rect($xr, $ypos, $cw, $ch, 'F', array(), $style['fgcolor']); 10662 } 10663 $xr += $cw; 10664 } 10665 $ypos += $ch; 10666 } 10667 } 10668 // restore original direction 10669 $this->rtl = $tempRTL; 10670 // restore previous settings 10671 $this->setGraphicVars($gvars); 10672 // set bottomcoordinates 10673 $this->img_rb_y = $y + $h; 10674 if ($this->rtl) { 10675 // set left side coordinate 10676 $this->img_rb_x = ($this->w - $x - $w); 10677 } else { 10678 // set right side coordinate 10679 $this->img_rb_x = $x + $w; 10680 } 10681 // set pointer to align the successive text/objects 10682 switch($align) { 10683 case 'T':{ 10684 $this->y = $y; 10685 $this->x = $this->img_rb_x; 10686 break; 10687 } 10688 case 'M':{ 10689 $this->y = $y + round($h/2); 10690 $this->x = $this->img_rb_x; 10691 break; 10692 } 10693 case 'B':{ 10694 $this->y = $this->img_rb_y; 10695 $this->x = $this->img_rb_x; 10696 break; 10697 } 10698 case 'N':{ 10699 $this->SetY($this->img_rb_y); 10700 break; 10701 } 10702 default:{ 10703 break; 10704 } 10705 } 10706 } 10707 10708 /** 10709 * Returns an array containing current margins: 10710 * <ul> 10711 <li>$ret['left'] = left margin</li> 10712 <li>$ret['right'] = right margin</li> 10713 <li>$ret['top'] = top margin</li> 10714 <li>$ret['bottom'] = bottom margin</li> 10715 <li>$ret['header'] = header margin</li> 10716 <li>$ret['footer'] = footer margin</li> 10717 <li>$ret['cell'] = cell margin</li> 10718 * </ul> 10719 * @return array containing all margins measures 10720 * @access public 10721 * @since 3.2.000 (2008-06-23) 10722 */ 10723 public function getMargins() { 10724 $ret = array( 10725 'left' => $this->lMargin, 10726 'right' => $this->rMargin, 10727 'top' => $this->tMargin, 10728 'bottom' => $this->bMargin, 10729 'header' => $this->header_margin, 10730 'footer' => $this->footer_margin, 10731 'cell' => $this->cMargin, 10732 ); 10733 return $ret; 10734 } 10735 10736 /** 10737 * Returns an array containing original margins: 10738 * <ul> 10739 <li>$ret['left'] = left margin</li> 10740 <li>$ret['right'] = right margin</li> 10741 * </ul> 10742 * @return array containing all margins measures 10743 * @access public 10744 * @since 4.0.012 (2008-07-24) 10745 */ 10746 public function getOriginalMargins() { 10747 $ret = array( 10748 'left' => $this->original_lMargin, 10749 'right' => $this->original_rMargin 10750 ); 10751 return $ret; 10752 } 10753 10754 /** 10755 * Returns the current font size. 10756 * @return current font size 10757 * @access public 10758 * @since 3.2.000 (2008-06-23) 10759 */ 10760 public function getFontSize() { 10761 return $this->FontSize; 10762 } 10763 10764 /** 10765 * Returns the current font size in points unit. 10766 * @return current font size in points unit 10767 * @access public 10768 * @since 3.2.000 (2008-06-23) 10769 */ 10770 public function getFontSizePt() { 10771 return $this->FontSizePt; 10772 } 10773 10774 /** 10775 * Returns the current font family name. 10776 * @return string current font family name 10777 * @access public 10778 * @since 4.3.008 (2008-12-05) 10779 */ 10780 public function getFontFamily() { 10781 return $this->FontFamily; 10782 } 10783 10784 /** 10785 * Returns the current font style. 10786 * @return string current font style 10787 * @access public 10788 * @since 4.3.008 (2008-12-05) 10789 */ 10790 public function getFontStyle() { 10791 return $this->FontStyle; 10792 } 10793 10794 /** 10795 * Prints a cell (rectangular area) with optional borders, background color and html text string. 10796 * 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 /> 10797 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting. 10798 * @param float $w Cell width. If 0, the cell extends up to the right margin. 10799 * @param float $h Cell minimum height. The cell extends automatically if needed. 10800 * @param float $x upper-left corner X coordinate 10801 * @param float $y upper-left corner Y coordinate 10802 * @param string $html html text to print. Default value: empty string. 10803 * @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> 10804 * @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> 10805 Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0. 10806 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. 10807 * @param boolean $reseth if true reset the last cell height (default true). 10808 * @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> 10809 * @param boolean $autopadding if true, uses internal padding and automatically adjust it to account for line width. 10810 * @access public 10811 * @uses MultiCell() 10812 * @see Multicell(), writeHTML() 10813 */ 10814 public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=0, $reseth=true, $align='', $autopadding=true) { 10815 return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true, $autopadding, 0); 10816 } 10817 10818 /** 10819 * Returns the HTML DOM array. 10820 * <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> 10821 * @param string $html html code 10822 * @return array 10823 * @access protected 10824 * @since 3.2.000 (2008-06-20) 10825 */ 10826 protected function getHtmlDomArray($html) { 10827 // remove all unsupported tags (the line below lists all supported tags) 10828 $html = strip_tags($html, '<marker/><a><b><blockquote><br><br/><dd><del><div><dl><dt><em><font><h1><h2><h3><h4><h5><h6><hr><i><img><li><ol><p><pre><small><span><strong><sub><sup><table><tcpdf><td><th><thead><tr><tt><u><ul>'); 10829 //replace some blank characters 10830 $html = preg_replace('/<pre/', '<xre', $html); // preserve pre tag 10831 $html = preg_replace('/<(table|tr|td|th|tcpdf|blockquote|dd|div|dt|h1|h2|h3|h4|h5|h6|br|hr|li|ol|ul|p)([^\>]*)>[\n\r\t]+/', '<\\1\\2>', $html); 10832 $html = preg_replace('@(\r\n|\r)@', "\n", $html); 10833 $repTable = array("\t" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\"); 10834 $html = strtr($html, $repTable); 10835 while (preg_match("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", $html)) { 10836 // preserve newlines on <pre> tag 10837 $html = preg_replace("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", "<xre\\1>\\2<br />\\3</pre>", $html); 10838 } 10839 $html = str_replace("\n", ' ', $html); 10840 // remove extra spaces from code 10841 $html = preg_replace('/[\s]+<\/(table|tr|td|th|ul|ol|li)>/', '</\\1>', $html); 10842 $html = preg_replace('/[\s]+<(tr|td|th|ul|ol|li|br)/', '<\\1', $html); 10843 $html = preg_replace('/<\/(table|tr|td|th|blockquote|dd|div|dt|h1|h2|h3|h4|h5|h6|hr|li|ol|ul|p)>[\s]+</', '</\\1><', $html); 10844 $html = preg_replace('/<\/(td|th)>/', '<marker style="font-size:0"/></\\1>', $html); 10845 $html = preg_replace('/<\/table>([\s]*)<marker style="font-size:0"\/>/', '</table>', $html); 10846 $html = preg_replace('/<img/', ' <img', $html); 10847 $html = preg_replace('/<img([^\>]*)>/xi', '<img\\1><span></span>', $html); 10848 $html = preg_replace('/<xre/', '<pre', $html); // restore pre tag 10849 // trim string 10850 $html = preg_replace('/^[\s]+/', '', $html); 10851 $html = preg_replace('/[\s]+$/', '', $html); 10852 // pattern for generic tag 10853 $tagpattern = '/(<[^>]+>)/'; 10854 // explodes the string 10855 $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); 10856 // count elements 10857 $maxel = count($a); 10858 $elkey = 0; 10859 $key = 0; 10860 // create an array of elements 10861 $dom = array(); 10862 $dom[$key] = array(); 10863 // set first void element 10864 $dom[$key]['tag'] = false; 10865 $dom[$key]['value'] = ''; 10866 $dom[$key]['parent'] = 0; 10867 $dom[$key]['fontname'] = $this->FontFamily; 10868 $dom[$key]['fontstyle'] = $this->FontStyle; 10869 $dom[$key]['fontsize'] = $this->FontSizePt; 10870 $dom[$key]['bgcolor'] = false; 10871 $dom[$key]['fgcolor'] = $this->fgcolor; 10872 $dom[$key]['align'] = ''; 10873 $dom[$key]['listtype'] = ''; 10874 $thead = false; // true when we are inside the THEAD tag 10875 ++$key; 10876 $level = array(); 10877 array_push($level, 0); // root 10878 while ($elkey < $maxel) { 10879 $dom[$key] = array(); 10880 $element = $a[$elkey]; 10881 $dom[$key]['elkey'] = $elkey; 10882 if (preg_match($tagpattern, $element)) { 10883 // html tag 10884 $element = substr($element, 1, -1); 10885 // get tag name 10886 preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag); 10887 $tagname = strtolower($tag[1]); 10888 // check if we are inside a table header 10889 if ($tagname == 'thead') { 10890 if ($element{0} == '/') { 10891 $thead = false; 10892 } else { 10893 $thead = true; 10894 } 10895 ++$elkey; 10896 continue; 10897 } 10898 $dom[$key]['tag'] = true; 10899 $dom[$key]['value'] = $tagname; 10900 if ($element{0} == '/') { 10901 // closing html tag 10902 $dom[$key]['opening'] = false; 10903 $dom[$key]['parent'] = end($level); 10904 array_pop($level); 10905 $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname']; 10906 $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle']; 10907 $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize']; 10908 $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor']; 10909 $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor']; 10910 $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align']; 10911 if (isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'])) { 10912 $dom[$key]['listtype'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype']; 10913 } 10914 // set the number of columns in table tag 10915 if (($dom[$key]['value'] == 'tr') AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) { 10916 $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols']; 10917 } 10918 if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) { 10919 $dom[($dom[$key]['parent'])]['content'] = ''; 10920 for ($i = ($dom[$key]['parent'] + 1); $i < $key; ++$i) { 10921 $dom[($dom[$key]['parent'])]['content'] .= $a[$dom[$i]['elkey']]; 10922 } 10923 $key = $i; 10924 } 10925 // store header rows on a new table 10926 if (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['thead'] == true)) { 10927 if ($this->empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) { 10928 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']]; 10929 } 10930 for ($i = $dom[$key]['parent']; $i <= $key; ++$i) { 10931 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']]; 10932 } 10933 } 10934 if (($dom[$key]['value'] == 'table') AND (!$this->empty_string($dom[($dom[$key]['parent'])]['thead']))) { 10935 $dom[($dom[$key]['parent'])]['thead'] .= '</table>'; 10936 } 10937 } else { 10938 // opening html tag 10939 $dom[$key]['opening'] = true; 10940 $dom[$key]['parent'] = end($level); 10941 if (substr($element, -1, 1) != '/') { 10942 // not self-closing tag 10943 array_push($level, $key); 10944 $dom[$key]['self'] = false; 10945 } else { 10946 $dom[$key]['self'] = true; 10947 } 10948 // copy some values from parent 10949 $parentkey = 0; 10950 if ($key > 0) { 10951 $parentkey = $dom[$key]['parent']; 10952 $dom[$key]['fontname'] = $dom[$parentkey]['fontname']; 10953 $dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle']; 10954 $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize']; 10955 $dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor']; 10956 $dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor']; 10957 $dom[$key]['align'] = $dom[$parentkey]['align']; 10958 $dom[$key]['listtype'] = $dom[$parentkey]['listtype']; 10959 } 10960 // get attributes 10961 preg_match_all('/([^=\s]*)=["]?([^"]*)["]?/', $element, $attr_array, PREG_PATTERN_ORDER); 10962 $dom[$key]['attribute'] = array(); // reset attribute array 10963 while (list($id, $name) = each($attr_array[1])) { 10964 $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id]; 10965 } 10966 // split style attributes 10967 if (isset($dom[$key]['attribute']['style'])) { 10968 // get style attributes 10969 preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER); 10970 $dom[$key]['style'] = array(); // reset style attribute array 10971 while (list($id, $name) = each($style_array[1])) { 10972 $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]); 10973 } 10974 // --- get some style attributes --- 10975 if (isset($dom[$key]['style']['font-family'])) { 10976 // font family 10977 if (isset($dom[$key]['style']['font-family'])) { 10978 $fontslist = split(',', strtolower($dom[$key]['style']['font-family'])); 10979 foreach ($fontslist as $font) { 10980 $font = trim(strtolower($font)); 10981 if (in_array($font, $this->fontlist) OR in_array($font, $this->fontkeys)) { 10982 $dom[$key]['fontname'] = $font; 10983 break; 10984 } 10985 } 10986 } 10987 } 10988 // list-style-type 10989 if (isset($dom[$key]['style']['list-style-type'])) { 10990 $dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type'])); 10991 if ($dom[$key]['listtype'] == 'inherit') { 10992 $dom[$key]['listtype'] = $dom[$parentkey]['listtype']; 10993 } 10994 } 10995 // font size 10996 if (isset($dom[$key]['style']['font-size'])) { 10997 $fsize = trim($dom[$key]['style']['font-size']); 10998 switch ($fsize) { 10999 // absolute-size 11000 case 'xx-small': { 11001 $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 4; 11002 break; 11003 } 11004 case 'x-small': { 11005 $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 3; 11006 break; 11007 } 11008 case 'small': { 11009 $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 2; 11010 break; 11011 } 11012 case 'medium': { 11013 $dom[$key]['fontsize'] = $dom[0]['fontsize']; 11014 break; 11015 } 11016 case 'large': { 11017 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 2; 11018 break; 11019 } 11020 case 'x-large': { 11021 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 4; 11022 break; 11023 } 11024 case 'xx-large': { 11025 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 6; 11026 break; 11027 } 11028 // relative-size 11029 case 'smaller': { 11030 $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] - 3; 11031 break; 11032 } 11033 case 'larger': { 11034 $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] + 3; 11035 break; 11036 } 11037 default: { 11038 $dom[$key]['fontsize'] = $this->getHTMLUnitToUnits($fsize, $dom[$parentkey]['fontsize'], 'pt', true); 11039 } 11040 } 11041 } 11042 // font style 11043 if (isset($dom[$key]['style']['font-weight']) AND (strtolower($dom[$key]['style']['font-weight']{0}) == 'b')) { 11044 $dom[$key]['fontstyle'] .= 'B'; 11045 } 11046 if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style']{0}) == 'i')) { 11047 $dom[$key]['fontstyle'] .= '"I'; 11048 } 11049 // font color 11050 if (isset($dom[$key]['style']['color']) AND (!$this->empty_string($dom[$key]['style']['color']))) { 11051 $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['color']); 11052 } 11053 // background color 11054 if (isset($dom[$key]['style']['background-color']) AND (!$this->empty_string($dom[$key]['style']['background-color']))) { 11055 $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['background-color']); 11056 } 11057 // text-decoration 11058 if (isset($dom[$key]['style']['text-decoration'])) { 11059 $decors = explode(' ', strtolower($dom[$key]['style']['text-decoration'])); 11060 foreach ($decors as $dec) { 11061 $dec = trim($dec); 11062 if (!$this->empty_string($dec)) { 11063 if ($dec{0} == 'u') { 11064 $dom[$key]['fontstyle'] .= 'U'; 11065 } elseif ($dec{0} == 'l') { 11066 $dom[$key]['fontstyle'] .= 'D'; 11067 } 11068 } 11069 } 11070 } 11071 // check for width attribute 11072 if (isset($dom[$key]['style']['width'])) { 11073 $dom[$key]['width'] = $dom[$key]['style']['width']; 11074 } 11075 // check for height attribute 11076 if (isset($dom[$key]['style']['height'])) { 11077 $dom[$key]['height'] = $dom[$key]['style']['height']; 11078 } 11079 // check for text alignment 11080 if (isset($dom[$key]['style']['text-align'])) { 11081 $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align']{0}); 11082 } 11083 // check for border attribute 11084 if (isset($dom[$key]['style']['border'])) { 11085 $dom[$key]['attribute']['border'] = $dom[$key]['style']['border']; 11086 } 11087 } 11088 // check for font tag 11089 if ($dom[$key]['value'] == 'font') { 11090 // font family 11091 if (isset($dom[$key]['attribute']['face'])) { 11092 $fontslist = split(',', strtolower($dom[$key]['attribute']['face'])); 11093 foreach ($fontslist as $font) { 11094 $font = trim(strtolower($font)); 11095 if (in_array($font, $this->fontlist) OR in_array($font, $this->fontkeys)) { 11096 $dom[$key]['fontname'] = $font; 11097 break; 11098 } 11099 } 11100 } 11101 // font size 11102 if (isset($dom[$key]['attribute']['size'])) { 11103 if ($key > 0) { 11104 if ($dom[$key]['attribute']['size']{0} == '+') { 11105 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1)); 11106 } elseif ($dom[$key]['attribute']['size']{0} == '-') { 11107 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1)); 11108 } else { 11109 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']); 11110 } 11111 } else { 11112 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']); 11113 } 11114 } 11115 } 11116 // force natural alignment for lists 11117 if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl')) 11118 AND (!isset($dom[$key]['align']) OR $this->empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) { 11119 if ($this->rtl) { 11120 $dom[$key]['align'] = 'R'; 11121 } else { 11122 $dom[$key]['align'] = 'L'; 11123 } 11124 } 11125 if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) { 11126 $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO; 11127 } 11128 if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) { 11129 $dom[$key]['fontstyle'] .= 'B'; 11130 } 11131 if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) { 11132 $dom[$key]['fontstyle'] .= 'I'; 11133 } 11134 if ($dom[$key]['value'] == 'u') { 11135 $dom[$key]['fontstyle'] .= 'U'; 11136 } 11137 if ($dom[$key]['value'] == 'del') { 11138 $dom[$key]['fontstyle'] .= 'D'; 11139 } 11140 if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) { 11141 $dom[$key]['fontname'] = $this->default_monospaced_font; 11142 } 11143 if (($dom[$key]['value']{0} == 'h') AND (intval($dom[$key]['value']{1}) > 0) AND (intval($dom[$key]['value']{1}) < 7)) { 11144 $headsize = (4 - intval($dom[$key]['value']{1})) * 2; 11145 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize; 11146 $dom[$key]['fontstyle'] .= 'B'; 11147 } 11148 if (($dom[$key]['value'] == 'table')) { 11149 $dom[$key]['rows'] = 0; // number of rows 11150 $dom[$key]['trids'] = array(); // IDs of TR elements 11151 $dom[$key]['thead'] = ''; // table header rows 11152 } 11153 if (($dom[$key]['value'] == 'tr')) { 11154 $dom[$key]['cols'] = 0; 11155 // store the number of rows on table element 11156 ++$dom[($dom[$key]['parent'])]['rows']; 11157 // store the TR elements IDs on table element 11158 array_push($dom[($dom[$key]['parent'])]['trids'], $key); 11159 if ($thead) { 11160 $dom[$key]['thead'] = true; 11161 } else { 11162 $dom[$key]['thead'] = false; 11163 } 11164 } 11165 if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) { 11166 if (isset($dom[$key]['attribute']['colspan'])) { 11167 $colspan = intval($dom[$key]['attribute']['colspan']); 11168 } else { 11169 $colspan = 1; 11170 } 11171 $dom[$key]['attribute']['colspan'] = $colspan; 11172 $dom[($dom[$key]['parent'])]['cols'] += $colspan; 11173 } 11174 // set foreground color attribute 11175 if (isset($dom[$key]['attribute']['color']) AND (!$this->empty_string($dom[$key]['attribute']['color']))) { 11176 $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['color']); 11177 } 11178 // set background color attribute 11179 if (isset($dom[$key]['attribute']['bgcolor']) AND (!$this->empty_string($dom[$key]['attribute']['bgcolor']))) { 11180 $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['bgcolor']); 11181 } 11182 // check for width attribute 11183 if (isset($dom[$key]['attribute']['width'])) { 11184 $dom[$key]['width'] = $dom[$key]['attribute']['width']; 11185 } 11186 // check for height attribute 11187 if (isset($dom[$key]['attribute']['height'])) { 11188 $dom[$key]['height'] = $dom[$key]['attribute']['height']; 11189 } 11190 // check for text alignment 11191 if (isset($dom[$key]['attribute']['align']) AND (!$this->empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) { 11192 $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align']{0}); 11193 } 11194 } // end opening tag 11195 } else { 11196 // text 11197 $dom[$key]['tag'] = false; 11198 $dom[$key]['value'] = stripslashes($this->unhtmlentities($element)); 11199 $dom[$key]['parent'] = end($level); 11200 } 11201 ++$elkey; 11202 ++$key; 11203 } 11204 return $dom; 11205 } 11206 11207 /** 11208 * Allows to preserve some HTML formatting (limited support).<br /> 11209 * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting. 11210 * 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, small, span, strong, sub, sup, table, td, th, tr, u, ul, 11211 * @param string $html text to display 11212 * @param boolean $ln if true add a new line after text (default = true) 11213 * @param int $fill Indicates if the background must be painted (true) or transparent (false). 11214 * @param boolean $reseth if true reset the last cell height (default false). 11215 * @param boolean $cell if true add the default cMargin space to each Write (default false). 11216 * @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> 11217 * @access public 11218 */ 11219 public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') { 11220 $gvars = $this->getGraphicVars(); 11221 // store current values 11222 $prevPage = $this->page; 11223 $prevlMargin = $this->lMargin; 11224 $prevrMargin = $this->rMargin; 11225 $curfontname = $this->FontFamily; 11226 $curfontstyle = $this->FontStyle; 11227 $curfontsize = $this->FontSizePt; 11228 $this->newline = true; 11229 $minstartliney = $this->y; 11230 $yshift = 0; 11231 $startlinepage = $this->page; 11232 $newline = true; 11233 $loop = 0; 11234 $curpos = 0; 11235 $blocktags = array('blockquote','br','dd','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','ul','tcpdf'); 11236 $this->premode = false; 11237 if (isset($this->PageAnnots[$this->page])) { 11238 $pask = count($this->PageAnnots[$this->page]); 11239 } else { 11240 $pask = 0; 11241 } 11242 if (isset($this->footerlen[$this->page])) { 11243 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page]; 11244 } else { 11245 $this->footerpos[$this->page] = $this->pagelen[$this->page]; 11246 } 11247 $startlinepos = $this->footerpos[$this->page]; 11248 $lalign = $align; 11249 $plalign = $align; 11250 if ($this->rtl) { 11251 $w = $this->x - $this->lMargin; 11252 } else { 11253 $w = $this->w - $this->rMargin - $this->x; 11254 } 11255 $w -= (2 * $this->cMargin); 11256 if ($cell) { 11257 if ($this->rtl) { 11258 $this->x -= $this->cMargin; 11259 } else { 11260 $this->x += $this->cMargin; 11261 } 11262 } 11263 if ($this->customlistindent >= 0) { 11264 $this->listindent = $this->customlistindent; 11265 } else { 11266 $this->listindent = $this->GetStringWidth('0000'); 11267 } 11268 $this->listnum = 0; 11269 if (($this->empty_string($this->lasth)) OR ($reseth)) { 11270 //set row height 11271 $this->lasth = $this->FontSize * $this->cell_height_ratio; 11272 } 11273 $dom = $this->getHtmlDomArray($html); 11274 $maxel = count($dom); 11275 $key = 0; 11276 while ($key < $maxel) { 11277 if ($dom[$key]['tag'] OR ($key == 0)) { 11278 if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) { 11279 $dom[$key]['align'] = ($this->rtl) ? 'R' : 'L'; 11280 } 11281 // vertically align image in line 11282 if ((!$this->newline) 11283 AND ($dom[$key]['value'] == 'img') 11284 AND (isset($dom[$key]['attribute']['height'])) 11285 AND ($dom[$key]['attribute']['height'] > 0)) { 11286 // get image height 11287 $imgh = $this->getHTMLUnitToUnits($dom[$key]['attribute']['height'], $this->lasth, 'px'); 11288 if (!$this->InFooter) { 11289 // check for page break 11290 $this->checkPageBreak($imgh); 11291 } 11292 if ($this->page > $startlinepage) { 11293 // fix lines splitted over two pages 11294 if (isset($this->footerlen[$startlinepage])) { 11295 $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 11296 } 11297 // line to be moved one page forward 11298 $pagebuff = $this->getPageBuffer($startlinepage); 11299 $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos)); 11300 $tstart = substr($pagebuff, 0, $startlinepos); 11301 $tend = substr($this->getPageBuffer($startlinepage), $curpos); 11302 // remove line start from previous page 11303 $this->setPageBuffer($startlinepage, $tstart.''.$tend); 11304 $pagebuff = $this->getPageBuffer($this->page); 11305 $tstart = substr($pagebuff, 0, $this->intmrk[$this->page]); 11306 $tend = substr($pagebuff, $this->intmrk[$this->page]); 11307 // add line start to current page 11308 $yshift = $minstartliney - $this->y; 11309 $try = sprintf('1 0 0 1 0 %.3F cm', ($yshift * $this->k)); 11310 $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend); 11311 // shift the annotations and links 11312 if (isset($this->PageAnnots[$startlinepage])) { 11313 foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) { 11314 if ($pak >= $pask) { 11315 $this->PageAnnots[$this->page][] = $pac; 11316 unset($this->PageAnnots[$startlinepage][$pak]); 11317 $npak = count($this->PageAnnots[$this->page]) - 1; 11318 $this->PageAnnots[$this->page][$npak]['y'] -= $yshift; 11319 } 11320 } 11321 } 11322 $startlinepos = $this->intmrk[$this->page]; 11323 $startlinepage = $this->page; 11324 $startliney = $this->y; 11325 } 11326 11327 $this->y += (($curfontsize / $this->k) - $imgh); 11328 $minstartliney = min($this->y, $minstartliney); 11329 11330 } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize'])) { 11331 // account for different font size 11332 $pfontname = $curfontname; 11333 $pfontstyle = $curfontstyle; 11334 $pfontsize = $curfontsize; 11335 $fontname = isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname; 11336 $fontstyle = isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle; 11337 $fontsize = isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize; 11338 if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)) { 11339 $this->SetFont($fontname, $fontstyle, $fontsize); 11340 $this->lasth = $this->FontSize * $this->cell_height_ratio; 11341 if (is_numeric($fontsize) AND ($fontsize > 0) 11342 AND is_numeric($curfontsize) AND ($curfontsize > 0) 11343 AND ($fontsize != $curfontsize) AND (!$this->newline) 11344 AND ($key < ($maxel - 1)) 11345 ) { 11346 if ((!$this->newline) AND ($this->page > $startlinepage)) { 11347 // fix lines splitted over two pages 11348 if (isset($this->footerlen[$startlinepage])) { 11349 $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 11350 } 11351 // line to be moved one page forward 11352 $pagebuff = $this->getPageBuffer($startlinepage); 11353 $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos)); 11354 $tstart = substr($pagebuff, 0, $startlinepos); 11355 $tend = substr($this->getPageBuffer($startlinepage), $curpos); 11356 // remove line start from previous page 11357 $this->setPageBuffer($startlinepage, $tstart.''.$tend); 11358 $pagebuff = $this->getPageBuffer($this->page); 11359 $tstart = substr($pagebuff, 0, $this->intmrk[$this->page]); 11360 $tend = substr($pagebuff, $this->intmrk[$this->page]); 11361 // add line start to current page 11362 $yshift = $minstartliney - $this->y; 11363 $try = sprintf('1 0 0 1 0 %.3F cm', ($yshift * $this->k)); 11364 $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend); 11365 // shift the annotations and links 11366 if (isset($this->PageAnnots[$startlinepage])) { 11367 foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) { 11368 if ($pak >= $pask) { 11369 $this->PageAnnots[$this->page][] = $pac; 11370 unset($this->PageAnnots[$startlinepage][$pak]); 11371 $npak = count($this->PageAnnots[$this->page]) - 1; 11372 $this->PageAnnots[$this->page][$npak]['y'] -= $yshift; 11373 } 11374 } 11375 } 11376 } 11377 $this->y += (($curfontsize - $fontsize) / $this->k); 11378 $minstartliney = min($this->y, $minstartliney); 11379 } 11380 $curfontname = $fontname; 11381 $curfontstyle = $fontstyle; 11382 $curfontsize = $fontsize; 11383 } 11384 } 11385 if (($plalign == 'J') AND (in_array($dom[$key]['value'], $blocktags))) { 11386 $plalign = ''; 11387 } 11388 // get current position on page buffer 11389 $curpos = $this->pagelen[$startlinepage]; 11390 if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) { 11391 $this->SetFillColorArray($dom[$key]['bgcolor']); 11392 $wfill = true; 11393 } else { 11394 $wfill = $fill | false; 11395 } 11396 if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) { 11397 $this->SetTextColorArray($dom[$key]['fgcolor']); 11398 } 11399 if (isset($dom[$key]['align'])) { 11400 $lalign = $dom[$key]['align']; 11401 } 11402 if ($this->empty_string($lalign)) { 11403 $lalign = $align; 11404 } 11405 } 11406 // align lines 11407 if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) { 11408 $newline = true; 11409 // we are at the beginning of a new line 11410 if (isset($startlinex)) { 11411 $yshift = $minstartliney - $startliney; 11412 if (($yshift > 0) OR ($this->page > $startlinepage)) { 11413 $yshift = 0; 11414 } 11415 if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl))))) OR ($yshift < 0)) { 11416 // the last line must be shifted to be aligned as requested 11417 $linew = abs($this->endlinex - $startlinex); 11418 $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos); 11419 if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) { 11420 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 11421 $midpos = min($opentagpos, $this->footerpos[$startlinepage]); 11422 } elseif (isset($opentagpos)) { 11423 $midpos = $opentagpos; 11424 } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) { 11425 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 11426 $midpos = $this->footerpos[$startlinepage]; 11427 } else { 11428 $midpos = 0; 11429 } 11430 if ($midpos > 0) { 11431 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos)); 11432 $pend = substr($this->getPageBuffer($startlinepage), $midpos); 11433 } else { 11434 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos); 11435 $pend = ''; 11436 } 11437 // calculate shifting amount 11438 $tw = $w; 11439 if ($this->lMargin != $prevlMargin) { 11440 $tw += ($prevlMargin - $this->lMargin); 11441 } 11442 if ($this->rMargin != $prevrMargin) { 11443 $tw += ($prevrMargin - $this->rMargin); 11444 } 11445 $mdiff = abs($tw - $linew); 11446 $t_x = 0; 11447 if ($plalign == 'C') { 11448 if ($this->rtl) { 11449 $t_x = -($mdiff / 2); 11450 } else { 11451 $t_x = ($mdiff / 2); 11452 } 11453 } elseif (($plalign == 'R') AND (!$this->rtl)) { 11454 // right alignment on LTR document 11455 $t_x = $mdiff; 11456 } elseif (($plalign == 'L') AND ($this->rtl)) { 11457 // left alignment on RTL document 11458 $t_x = -$mdiff; 11459 } elseif (($plalign == 'J') AND ($plalign == $lalign)) { 11460 // Justification 11461 if ($this->rtl OR $this->tmprtl) { 11462 $t_x = $this->lMargin - $this->endlinex; 11463 } 11464 $no = 0; 11465 $ns = 0; 11466 $pmidtemp = $pmid; 11467 // escape special characters 11468 $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp); 11469 $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp); 11470 // search spaces 11471 if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER)) { 11472 $maxkk = count($lnstring[1]) - 1; 11473 for ($kk=0; $kk <= $maxkk; ++$kk) { 11474 // restore special characters 11475 $lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]); 11476 $lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]); 11477 if ($kk == $maxkk) { 11478 if ($this->rtl OR $this->tmprtl) { 11479 $tvalue = ltrim($lnstring[1][$kk]); 11480 } else { 11481 $tvalue = rtrim($lnstring[1][$kk]); 11482 } 11483 } else { 11484 $tvalue = $lnstring[1][$kk]; 11485 } 11486 // count spaces on line 11487 $no += substr_count($lnstring[1][$kk], chr(32)); 11488 $ns += substr_count($tvalue, chr(32)); 11489 } 11490 if ($this->rtl OR $this->tmprtl) { 11491 $t_x = $this->lMargin - $this->endlinex - (($no - $ns - 1) * $this->GetStringWidth(chr(32))); 11492 } 11493 // calculate additional space to add to each space 11494 $spacewidth = (($tw - $linew + (($no - $ns) * $this->GetStringWidth(chr(32)))) / ($ns?$ns:1)) * $this->k; 11495 $spacewidthu = ($tw - $linew + ($no * $this->GetStringWidth(chr(32)))) / ($ns?$ns:1) / $this->FontSize / $this->k; 11496 $nsmax = $ns; 11497 $ns = 0; 11498 reset($lnstring); 11499 $offset = 0; 11500 $strcount = 0; 11501 $prev_epsposbeg = 0; 11502 global $spacew; 11503 while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE, $offset) == 1) { 11504 if ($this->rtl OR $this->tmprtl) { 11505 $spacew = ($spacewidth * ($nsmax - $ns)); 11506 } else { 11507 $spacew = ($spacewidth * $ns); 11508 } 11509 $offset = $strpiece[2][1] + strlen($strpiece[2][0]); 11510 $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, $offset); 11511 $epsposend = strpos($pmid, $this->epsmarker.'Q', $offset) + strlen($this->epsmarker.'Q'); 11512 if ((($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend)) 11513 OR (($epsposbeg === false) AND ($epsposend > 0) AND ($offset < $epsposend))) { 11514 // shift EPS images 11515 $trx = sprintf('1 0 0 1 %.3F 0 cm', $spacew); 11516 $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, ($prev_epsposbeg - 6)); 11517 $pmid_b = substr($pmid, 0, $epsposbeg); 11518 $pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg)); 11519 $pmid_e = substr($pmid, $epsposend); 11520 $pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e; 11521 $offset = $epsposend; 11522 continue; 11523 } 11524 $prev_epsposbeg = $epsposbeg; 11525 $currentxpos = 0; 11526 // shift blocks of code 11527 switch ($strpiece[2][0]) { 11528 case 'Td': 11529 case 'cm': 11530 case 'm': 11531 case 'l': { 11532 // get current X position 11533 preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches); 11534 $currentxpos = $xmatches[1]; 11535 if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) { 11536 if ($strcount == $maxkk) { 11537 if ($this->rtl OR $this->tmprtl) { 11538 $tvalue = $lnstring[1][$strcount]; 11539 } else { 11540 $tvalue = rtrim($lnstring[1][$strcount]); 11541 } 11542 } else { 11543 $tvalue = $lnstring[1][$strcount]; 11544 } 11545 $ns += substr_count($tvalue, chr(32)); 11546 ++$strcount; 11547 } 11548 if ($this->rtl OR $this->tmprtl) { 11549 $spacew = ($spacewidth * ($nsmax - $ns)); 11550 } 11551 // justify block 11552 $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', 11553 create_function('$matches', 'global $spacew; 11554 $newx = sprintf("%.2F",(floatval($matches[1]) + $spacew)); 11555 return "".$newx." ".$matches[2]." x*#!#*x".$matches[3].$matches[4];'), $pmid, 1); 11556 break; 11557 } 11558 case 're': { 11559 // get current X position 11560 preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches); 11561 $currentxpos = $xmatches[1]; 11562 // justify block 11563 $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', 11564 create_function('$matches', 'global $spacew; 11565 $newx = sprintf("%.2F",(floatval($matches[1]) + $spacew)); 11566 return "".$newx." ".$matches[2]." ".$matches[3]." ".$matches[4]." x*#!#*x".$matches[5].$matches[6];'), $pmid, 1); 11567 break; 11568 } 11569 case 'c': { 11570 // get current X position 11571 preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches); 11572 $currentxpos = $xmatches[1]; 11573 // justify block 11574 $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', 11575 create_function('$matches', 'global $spacew; 11576 $newx1 = sprintf("%.3F",(floatval($matches[1]) + $spacew)); 11577 $newx2 = sprintf("%.3F",(floatval($matches[3]) + $spacew)); 11578 $newx3 = sprintf("%.3F",(floatval($matches[5]) + $spacew)); 11579 return "".$newx1." ".$matches[2]." ".$newx2." ".$matches[4]." ".$newx3." ".$matches[6]." x*#!#*x".$matches[7].$matches[8];'), $pmid, 1); 11580 break; 11581 } 11582 } 11583 // shift the annotations and links 11584 if (isset($this->PageAnnots[$this->page])) { 11585 foreach ($this->PageAnnots[$this->page] as $pak => $pac) { 11586 if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) { 11587 $this->PageAnnots[$this->page][$pak]['x'] += ($spacew / $this->k); 11588 $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k); 11589 break; 11590 } 11591 } 11592 } 11593 } // end of while 11594 // remove markers 11595 $pmid = str_replace('x*#!#*x', '', $pmid); 11596 if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) { 11597 // multibyte characters 11598 $spacew = $spacewidthu; 11599 $pmidtemp = $pmid; 11600 // escape special characters 11601 $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp); 11602 $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp); 11603 $pmid = preg_replace_callback("/\[\(([^\)]*)\)\]/x", 11604 create_function('$matches', 'global $spacew; 11605 $matches[1] = str_replace("#!#OP#!#", "(", $matches[1]); 11606 $matches[1] = str_replace("#!#CP#!#", ")", $matches[1]); 11607 return "[(".str_replace(chr(0).chr(32), ") ".(-2830 * $spacew)." (", $matches[1]).")]";'), $pmidtemp); 11608 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend); 11609 $endlinepos = strlen($pstart."\n".$pmid."\n"); 11610 } else { 11611 // non-unicode (single-byte characters) 11612 $rs = sprintf("%.3F Tw", $spacewidth); 11613 $pmid = preg_replace("/\[\(/x", $rs.' [(', $pmid); 11614 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend); 11615 $endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n"); 11616 } 11617 } 11618 } // end of J 11619 if (($t_x != 0) OR ($yshift < 0)) { 11620 // shift the line 11621 $trx = sprintf('1 0 0 1 %.3F %.3F cm', ($t_x * $this->k), ($yshift * $this->k)); 11622 $this->setPageBuffer($startlinepage, $pstart."\nq\n".$trx."\n".$pmid."\nQ\n".$pend); 11623 $endlinepos = strlen($pstart."\nq\n".$trx."\n".$pmid."\nQ\n"); 11624 // shift the annotations and links 11625 if (isset($this->PageAnnots[$this->page])) { 11626 foreach ($this->PageAnnots[$this->page] as $pak => $pac) { 11627 if ($pak >= $pask) { 11628 $this->PageAnnots[$this->page][$pak]['x'] += $t_x; 11629 $this->PageAnnots[$this->page][$pak]['y'] -= $yshift; 11630 } 11631 } 11632 } 11633 $this->y -= $yshift; 11634 } 11635 } 11636 } 11637 $this->newline = false; 11638 $pbrk = $this->checkPageBreak($this->lasth); 11639 $this->SetFont($fontname, $fontstyle, $fontsize); 11640 if ($wfill) { 11641 $this->SetFillColorArray($this->bgcolor); 11642 } 11643 $startlinex = $this->x; 11644 $startliney = $this->y; 11645 $minstartliney = $this->y; 11646 $startlinepage = $this->page; 11647 if (isset($endlinepos) AND (!$pbrk)) { 11648 $startlinepos = $endlinepos; 11649 unset($endlinepos); 11650 } else { 11651 if (isset($this->footerlen[$this->page])) { 11652 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page]; 11653 } else { 11654 $this->footerpos[$this->page] = $this->pagelen[$this->page]; 11655 } 11656 $startlinepos = $this->footerpos[$this->page]; 11657 } 11658 $plalign = $lalign; 11659 if (isset($this->PageAnnots[$this->page])) { 11660 $pask = count($this->PageAnnots[$this->page]); 11661 } else { 11662 $pask = 0; 11663 } 11664 } 11665 if (isset($opentagpos)) { 11666 unset($opentagpos); 11667 } 11668 if ($dom[$key]['tag']) { 11669 if ($dom[$key]['opening']) { 11670 if ($dom[$key]['value'] == 'table') { 11671 if ($this->rtl) { 11672 $wtmp = $this->x - $this->lMargin; 11673 } else { 11674 $wtmp = $this->w - $this->rMargin - $this->x; 11675 } 11676 $wtmp -= (2 * $this->cMargin); 11677 // calculate cell width 11678 if (isset($dom[$key]['width'])) { 11679 $table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px'); 11680 } else { 11681 $table_width = $wtmp; 11682 } 11683 } 11684 // table content is handled in a special way 11685 if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) { 11686 $trid = $dom[$key]['parent']; 11687 $table_el = $dom[$trid]['parent']; 11688 if (!isset($dom[$table_el]['cols'])) { 11689 $dom[$table_el]['cols'] = $trid['cols']; 11690 } 11691 $oldmargin = $this->cMargin; 11692 if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) { 11693 $currentcmargin = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px'); 11694 } else { 11695 $currentcmargin = 0; 11696 } 11697 $this->cMargin = $currentcmargin; 11698 if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellspacing'])) { 11699 $cellspacing = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellspacing'], 1, 'px'); 11700 } else { 11701 $cellspacing = 0; 11702 } 11703 if ($this->rtl) { 11704 $cellspacingx = -$cellspacing; 11705 } else { 11706 $cellspacingx = $cellspacing; 11707 } 11708 $colspan = $dom[$key]['attribute']['colspan']; 11709 $wtmp = ($colspan * ($table_width / $dom[$table_el]['cols'])); 11710 if (isset($dom[$key]['width'])) { 11711 $cellw = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px'); 11712 } else { 11713 $cellw = $wtmp; 11714 } 11715 if (isset($dom[$key]['height'])) { 11716 // minimum cell height 11717 $cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px'); 11718 } else { 11719 $cellh = 0; 11720 } 11721 $cellw -= $cellspacing; 11722 if (isset($dom[$key]['content'])) { 11723 $cell_content = $dom[$key]['content']; 11724 } else { 11725 $cell_content = ' '; 11726 } 11727 $tagtype = $dom[$key]['value']; 11728 $parentid = $key; 11729 while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) { 11730 // move $key index forward 11731 ++$key; 11732 } 11733 if (!isset($dom[$trid]['startpage'])) { 11734 $dom[$trid]['startpage'] = $this->page; 11735 } else { 11736 $this->setPage($dom[$trid]['startpage']); 11737 } 11738 if (!isset($dom[$trid]['starty'])) { 11739 $dom[$trid]['starty'] = $this->y; 11740 } else { 11741 $this->y = $dom[$trid]['starty']; 11742 } 11743 if (!isset($dom[$trid]['startx'])) { 11744 $dom[$trid]['startx'] = $this->x; 11745 } 11746 $this->x += ($cellspacingx / 2); 11747 if (isset($dom[$parentid]['attribute']['rowspan'])) { 11748 $rowspan = intval($dom[$parentid]['attribute']['rowspan']); 11749 } else { 11750 $rowspan = 1; 11751 } 11752 // skip row-spanned cells started on the previous rows 11753 if (isset($dom[$table_el]['rowspans'])) { 11754 $rsk = 0; 11755 $rskmax = count($dom[$table_el]['rowspans']); 11756 while ($rsk < $rskmax) { 11757 $trwsp = $dom[$table_el]['rowspans'][$rsk]; 11758 $rsstartx = $trwsp['startx']; 11759 $rsendx = $trwsp['endx']; 11760 // account for margin changes 11761 if ($trwsp['startpage'] < $this->page) { 11762 if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$trwsp['startpage']]['orm'])) { 11763 $dl = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$trwsp['startpage']]['orm']); 11764 $rsstartx -= $dl; 11765 $rsendx -= $dl; 11766 } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$trwsp['startpage']]['olm'])) { 11767 $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$trwsp['startpage']]['olm']); 11768 $rsstartx += $dl; 11769 $rsendx += $dl; 11770 } 11771 } 11772 if (($trwsp['rowspan'] > 0) 11773 AND ($rsstartx > ($this->x - $cellspacing - $currentcmargin - $this->feps)) 11774 AND ($rsstartx < ($this->x + $cellspacing + $currentcmargin + $this->feps)) 11775 AND (($trwsp['starty'] < ($this->y - $this->feps)) OR ($trwsp['startpage'] < $this->page))) { 11776 // set the starting X position of the current cell 11777 $this->x = $rsendx + $cellspacingx; 11778 if (($trwsp['rowspan'] == 1) 11779 AND (isset($dom[$trid]['endy'])) 11780 AND (isset($dom[$trid]['endpage'])) 11781 AND ($trwsp['endpage'] == $dom[$trid]['endpage'])) { 11782 // set ending Y position for row 11783 $dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']); 11784 $dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy']; 11785 } 11786 $rsk = 0; 11787 } else { 11788 ++$rsk; 11789 } 11790 } 11791 } 11792 // add rowspan information to table element 11793 if ($rowspan > 1) { 11794 if (isset($this->footerlen[$this->page])) { 11795 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page]; 11796 } else { 11797 $this->footerpos[$this->page] = $this->pagelen[$this->page]; 11798 } 11799 $trintmrkpos = $this->footerpos[$this->page]; 11800 $trsid = array_push($dom[$table_el]['rowspans'], array('trid' => $trid, 'rowspan' => $rowspan, 'mrowspan' => $rowspan, 'colspan' => $colspan, 'startpage' => $this->page, 'startx' => $this->x, 'starty' => $this->y, 'intmrkpos' => $trintmrkpos)); 11801 } 11802 $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x)); 11803 if ($rowspan > 1) { 11804 $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1); 11805 } 11806 // push background colors 11807 if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) { 11808 $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor']; 11809 } 11810 $prevLastH = $this->lasth; 11811 // ****** write the cell content ****** 11812 $this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true); 11813 $this->lasth = $prevLastH; 11814 $this->cMargin = $oldmargin; 11815 $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x; 11816 // update the end of row position 11817 if ($rowspan <= 1) { 11818 if (isset($dom[$trid]['endy'])) { 11819 if ($this->page == $dom[$trid]['endpage']) { 11820 $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']); 11821 } elseif ($this->page > $dom[$trid]['endpage']) { 11822 $dom[$trid]['endy'] = $this->y; 11823 } 11824 } else { 11825 $dom[$trid]['endy'] = $this->y; 11826 } 11827 if (isset($dom[$trid]['endpage'])) { 11828 $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']); 11829 } else { 11830 $dom[$trid]['endpage'] = $this->page; 11831 } 11832 } else { 11833 // account for row-spanned cells 11834 $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x; 11835 $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y; 11836 $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page; 11837 } 11838 if (isset($dom[$table_el]['rowspans'])) { 11839 // update endy and endpage on rowspanned cells 11840 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) { 11841 if ($trwsp['rowspan'] > 0) { 11842 if (isset($dom[$trid]['endpage'])) { 11843 if ($trwsp['endpage'] == $dom[$trid]['endpage']) { 11844 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']); 11845 } elseif ($trwsp['endpage'] < $dom[$trid]['endpage']) { 11846 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy']; 11847 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage']; 11848 } else { 11849 $dom[$trid]['endy'] = $this->pagedim[$dom[$trid]['endpage']]['hk'] - $this->pagedim[$dom[$trid]['endpage']]['bm']; 11850 } 11851 } 11852 } 11853 } 11854 } 11855 $this->x += ($cellspacingx / 2); 11856 } else { 11857 // opening tag (or self-closing tag) 11858 if (!isset($opentagpos)) { 11859 if (!$this->InFooter) { 11860 if (isset($this->footerlen[$this->page])) { 11861 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page]; 11862 } else { 11863 $this->footerpos[$this->page] = $this->pagelen[$this->page]; 11864 } 11865 $opentagpos = $this->footerpos[$this->page]; 11866 } 11867 } 11868 $this->openHTMLTagHandler($dom, $key, $cell); 11869 } 11870 } else { 11871 // closing tag 11872 $this->closeHTMLTagHandler($dom, $key, $cell); 11873 } 11874 } elseif (strlen($dom[$key]['value']) > 0) { 11875 // print list-item 11876 if (!$this->empty_string($this->lispacer)) { 11877 $this->SetFont($pfontname, $pfontstyle, $pfontsize); 11878 $this->lasth = $this->FontSize * $this->cell_height_ratio; 11879 $minstartliney = $this->y; 11880 $this->putHtmlListBullet($this->listnum, $this->lispacer, $pfontsize); 11881 $this->SetFont($curfontname, $curfontstyle, $curfontsize); 11882 $this->lasth = $this->FontSize * $this->cell_height_ratio; 11883 if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) { 11884 $this->y += (($pfontsize - $curfontsize) / $this->k); 11885 $minstartliney = min($this->y, $minstartliney); 11886 } 11887 } 11888 // text 11889 $this->htmlvspace = 0; 11890 if ((!$this->premode) AND ($this->rtl OR $this->tmprtl)) { 11891 // reverse spaces order 11892 $len1 = strlen($dom[$key]['value']); 11893 $lsp = $len1 - strlen(ltrim($dom[$key]['value'])); 11894 $rsp = $len1 - strlen(rtrim($dom[$key]['value'])); 11895 $tmpstr = ''; 11896 if ($rsp > 0) { 11897 $tmpstr .= substr($dom[$key]['value'], -$rsp); 11898 } 11899 $tmpstr .= trim($dom[$key]['value']); 11900 if ($lsp > 0) { 11901 $tmpstr .= substr($dom[$key]['value'], 0, $lsp); 11902 } 11903 $dom[$key]['value'] = $tmpstr; 11904 } 11905 if ($newline) { 11906 if (!$this->premode) { 11907 if (($this->rtl OR $this->tmprtl)) { 11908 $dom[$key]['value'] = rtrim($dom[$key]['value']); 11909 } else { 11910 $dom[$key]['value'] = ltrim($dom[$key]['value']); 11911 } 11912 } 11913 $newline = false; 11914 $firstblock = true; 11915 } else { 11916 $firstblock = false; 11917 } 11918 $strrest = ''; 11919 if (!empty($this->HREF) AND (isset($this->HREF['url']))) { 11920 // HTML <a> Link 11921 $strrest = $this->addHtmlLink($this->HREF['url'], $dom[$key]['value'], $wfill, true, $this->HREF['color'], $this->HREF['style']); 11922 } else { 11923 $ctmpmargin = $this->cMargin; 11924 $this->cMargin = 0; 11925 // ****** write only until the end of the line and get the rest ****** 11926 $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock); 11927 $this->cMargin = $ctmpmargin; 11928 } 11929 if (strlen($strrest) > 0) { 11930 // store the remaining string on the previous $key position 11931 $this->newline = true; 11932 if ($cell) { 11933 if ($this->rtl) { 11934 $this->x -= $this->cMargin; 11935 } else { 11936 $this->x += $this->cMargin; 11937 } 11938 } 11939 if ($strrest == $dom[$key]['value']) { 11940 // used to avoid infinite loop 11941 ++$loop; 11942 } else { 11943 $loop = 0; 11944 } 11945 $dom[$key]['value'] = ltrim($strrest); 11946 if ($loop < 3) { 11947 --$key; 11948 } 11949 } else { 11950 $loop = 0; 11951 } 11952 } 11953 ++$key; 11954 } // end for each $key 11955 // align the last line 11956 if (isset($startlinex)) { 11957 $yshift = $minstartliney - $startliney; 11958 if (($yshift > 0) OR ($this->page > $startlinepage)) { 11959 $yshift = 0; 11960 } 11961 if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl))))) OR ($yshift < 0)) { 11962 // the last line must be shifted to be aligned as requested 11963 $linew = abs($this->endlinex - $startlinex); 11964 $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos); 11965 if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) { 11966 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 11967 $midpos = min($opentagpos, $this->footerpos[$startlinepage]); 11968 } elseif (isset($opentagpos)) { 11969 $midpos = $opentagpos; 11970 } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) { 11971 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; 11972 $midpos = $this->footerpos[$startlinepage]; 11973 } else { 11974 $midpos = 0; 11975 } 11976 if ($midpos > 0) { 11977 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos)); 11978 $pend = substr($this->getPageBuffer($startlinepage), $midpos); 11979 } else { 11980 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos); 11981 $pend = ''; 11982 } 11983 // calculate shifting amount 11984 $tw = $w; 11985 if ($this->lMargin != $prevlMargin) { 11986 $tw += ($prevlMargin - $this->lMargin); 11987 } 11988 if ($this->rMargin != $prevrMargin) { 11989 $tw += ($prevrMargin - $this->rMargin); 11990 } 11991 $mdiff = abs($tw - $linew); 11992 if ($plalign == 'C') { 11993 if ($this->rtl) { 11994 $t_x = -($mdiff / 2); 11995 } else { 11996 $t_x = ($mdiff / 2); 11997 } 11998 } elseif (($plalign == 'R') AND (!$this->rtl)) { 11999 // right alignment on LTR document 12000 $t_x = $mdiff; 12001 } elseif (($plalign == 'L') AND ($this->rtl)) { 12002 // left alignment on RTL document 12003 $t_x = -$mdiff; 12004 } else { 12005 $t_x = 0; 12006 } 12007 if (($t_x != 0) OR ($yshift < 0)) { 12008 // shift the line 12009 $trx = sprintf('1 0 0 1 %.3F %.3F cm', ($t_x * $this->k), ($yshift * $this->k)); 12010 $this->setPageBuffer($startlinepage, $pstart."\nq\n".$trx."\n".$pmid."\nQ\n".$pend); 12011 $endlinepos = strlen($pstart."\nq\n".$trx."\n".$pmid."\nQ\n"); 12012 // shift the annotations and links 12013 if (isset($this->PageAnnots[$this->page])) { 12014 foreach ($this->PageAnnots[$this->page] as $pak => $pac) { 12015 if ($pak >= $pask) { 12016 $this->PageAnnots[$this->page][$pak]['x'] += $t_x; 12017 $this->PageAnnots[$this->page][$pak]['y'] -= $yshift; 12018 } 12019 } 12020 } 12021 $this->y -= $yshift; 12022 } 12023 } 12024 } 12025 if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) { 12026 $this->Ln($this->lasth); 12027 } 12028 // restore previous values 12029 $this->setGraphicVars($gvars); 12030 if ($this->page > $prevPage) { 12031 $this->lMargin = $this->pagedim[$this->page]['olm']; 12032 $this->rMargin = $this->pagedim[$this->page]['orm']; 12033 } 12034 unset($dom); 12035 } 12036 12037 /** 12038 * Process opening tags. 12039 * @param array $dom html dom array 12040 * @param int $key current element id 12041 * @param boolean $cell if true add the default cMargin space to each new line (default false). 12042 * @access protected 12043 */ 12044 protected function openHTMLTagHandler(&$dom, $key, $cell=false) { 12045 $tag = $dom[$key]; 12046 $parent = $dom[($dom[$key]['parent'])]; 12047 $firstorlast = ($key == 1); 12048 // check for text direction attribute 12049 if (isset($tag['attribute']['dir'])) { 12050 $this->tmprtl = $tag['attribute']['dir'] == 'rtl' ? 'R' : 'L'; 12051 } else { 12052 $this->tmprtl = false; 12053 } 12054 //Opening tag 12055 switch($tag['value']) { 12056 case 'table': { 12057 $cp = 0; 12058 $cs = 0; 12059 $dom[$key]['rowspans'] = array(); 12060 if (!$this->empty_string($dom[$key]['thead'])) { 12061 // set table header 12062 $this->thead = $dom[$key]['thead']; 12063 } 12064 if (isset($tag['attribute']['cellpadding'])) { 12065 $cp = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px'); 12066 $this->oldcMargin = $this->cMargin; 12067 $this->cMargin = $cp; 12068 } 12069 if (isset($tag['attribute']['cellspacing'])) { 12070 $cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px'); 12071 } 12072 $this->checkPageBreak((2 * $cp) + (2 * $cs) + $this->lasth); 12073 break; 12074 } 12075 case 'tr': { 12076 // array of columns positions 12077 $dom[$key]['cellpos'] = array(); 12078 break; 12079 } 12080 case 'hr': { 12081 $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false); 12082 $this->htmlvspace = 0; 12083 $wtmp = $this->w - $this->lMargin - $this->rMargin; 12084 if ((isset($tag['attribute']['width'])) AND ($tag['attribute']['width'] != '')) { 12085 $hrWidth = $this->getHTMLUnitToUnits($tag['attribute']['width'], $wtmp, 'px'); 12086 } else { 12087 $hrWidth = $wtmp; 12088 } 12089 $x = $this->GetX(); 12090 $y = $this->GetY(); 12091 $prevlinewidth = $this->GetLineWidth(); 12092 $this->Line($x, $y, $x + $hrWidth, $y); 12093 $this->SetLineWidth($prevlinewidth); 12094 $this->addHTMLVertSpace(1, $cell, '', !isset($dom[($key + 1)]), $tag['value'], false); 12095 break; 12096 } 12097 case 'a': { 12098 if (array_key_exists('href', $tag['attribute'])) { 12099 $this->HREF['url'] = $tag['attribute']['href']; 12100 } 12101 $this->HREF['color'] = $this->htmlLinkColorArray; 12102 $this->HREF['style'] = $this->htmlLinkFontStyle; 12103 if (array_key_exists('style', $tag['attribute'])) { 12104 // get style attributes 12105 preg_match_all('/([^;:\s]*):([^;]*)/', $tag['attribute']['style'], $style_array, PREG_PATTERN_ORDER); 12106 $astyle = array(); 12107 while (list($id, $name) = each($style_array[1])) { 12108 $name = strtolower($name); 12109 $astyle[$name] = trim($style_array[2][$id]); 12110 } 12111 if (isset($astyle['color'])) { 12112 $this->HREF['color'] = $this->convertHTMLColorToDec($astyle['color']); 12113 } 12114 if (isset($astyle['text-decoration'])) { 12115 $this->HREF['style'] = ''; 12116 $decors = explode(' ', strtolower($astyle['text-decoration'])); 12117 foreach ($decors as $dec) { 12118 $dec = trim($dec); 12119 if (!$this->empty_string($dec)) { 12120 if ($dec{0} == 'u') { 12121 $this->HREF['style'] .= 'U'; 12122 } elseif ($dec{0} == 'l') { 12123 $this->HREF['style'] .= 'D'; 12124 } 12125 } 12126 } 12127 } 12128 } 12129 break; 12130 } 12131 case 'img': { 12132 if (isset($tag['attribute']['src'])) { 12133 // replace relative path with real server path 12134 if ($tag['attribute']['src'][0] == '/') { 12135 $tag['attribute']['src'] = $_SERVER['DOCUMENT_ROOT'].$tag['attribute']['src']; 12136 } 12137 $tag['attribute']['src'] = urldecode($tag['attribute']['src']); 12138 $tag['attribute']['src'] = str_replace(K_PATH_URL, K_PATH_MAIN, $tag['attribute']['src']); 12139 if (!isset($tag['attribute']['width'])) { 12140 $tag['attribute']['width'] = 0; 12141 } 12142 if (!isset($tag['attribute']['height'])) { 12143 $tag['attribute']['height'] = 0; 12144 } 12145 //if (!isset($tag['attribute']['align'])) { 12146 // the only alignment supported is "bottom" 12147 // further development is required for other modes. 12148 $tag['attribute']['align'] = 'bottom'; 12149 //} 12150 switch($tag['attribute']['align']) { 12151 case 'top': { 12152 $align = 'T'; 12153 break; 12154 } 12155 case 'middle': { 12156 $align = 'M'; 12157 break; 12158 } 12159 case 'bottom': { 12160 $align = 'B'; 12161 break; 12162 } 12163 default: { 12164 $align = 'B'; 12165 break; 12166 } 12167 } 12168 $fileinfo = pathinfo($tag['attribute']['src']); 12169 if (isset($fileinfo['extension']) AND (!$this->empty_string($fileinfo['extension']))) { 12170 $type = strtolower($fileinfo['extension']); 12171 } 12172 $prevy = $this->y; 12173 $xpos = $this->GetX(); 12174 if (isset($dom[($key - 1)]) AND ($dom[($key - 1)]['value'] == ' ')) { 12175 if ($this->rtl) { 12176 $xpos += $this->GetStringWidth(' '); 12177 } else { 12178 $xpos -= $this->GetStringWidth(' '); 12179 } 12180 } 12181 $imglink = ''; 12182 if (isset($this->HREF['url']) AND !$this->empty_string($this->HREF['url'])) { 12183 $imglink = $this->HREF['url']; 12184 if ($imglink{0} == '#') { 12185 // convert url to internal link 12186 $page = intval(substr($imglink, 1)); 12187 $imglink = $this->AddLink(); 12188 $this->SetLink($imglink, 0, $page); 12189 } 12190 } 12191 $border = 0; 12192 if (isset($tag['attribute']['border']) AND !empty($tag['attribute']['border'])) { 12193 // currently only support 1 (frame) or a combination of 'LTRB' 12194 $border = $tag['attribute']['border']; 12195 } 12196 $iw = ''; 12197 if (isset($tag['attribute']['width'])) { 12198 $iw = $this->getHTMLUnitToUnits($tag['attribute']['width'], 1, 'px', false); 12199 } 12200 $ih = ''; 12201 if (isset($tag['attribute']['height'])) { 12202 $ih = $this->getHTMLUnitToUnits($tag['attribute']['height'], 1, 'px', false); 12203 } 12204 if (($type == 'eps') OR ($type == 'ai')) { 12205 $this->ImageEps($tag['attribute']['src'], $xpos, $this->GetY(), $iw, $ih, $imglink, true, $align, '', $border); 12206 } else { 12207 $this->Image($tag['attribute']['src'], $xpos, $this->GetY(), $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border); 12208 } 12209 switch($align) { 12210 case 'T': { 12211 $this->y = $prevy; 12212 break; 12213 } 12214 case 'M': { 12215 $this->y = (($this->img_rb_y + $prevy - ($tag['fontsize'] / $this->k)) / 2) ; 12216 break; 12217 } 12218 case 'B': { 12219 $this->y = $this->img_rb_y - ($tag['fontsize'] / $this->k); 12220 break; 12221 } 12222 } 12223 } 12224 break; 12225 } 12226 case 'dl': { 12227 ++$this->listnum; 12228 $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], false); 12229 break; 12230 } 12231 case 'dt': { 12232 $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false); 12233 break; 12234 } 12235 case 'dd': { 12236 if ($this->rtl) { 12237 $this->rMargin += $this->listindent; 12238 } else { 12239 $this->lMargin += $this->listindent; 12240 } 12241 $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false); 12242 break; 12243 } 12244 case 'ul': 12245 case 'ol': { 12246 $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], false); 12247 $this->htmlvspace = 0; 12248 ++$this->listnum; 12249 if ($tag['value'] == 'ol') { 12250 $this->listordered[$this->listnum] = true; 12251 } else { 12252 $this->listordered[$this->listnum] = false; 12253 } 12254 if (isset($tag['attribute']['start'])) { 12255 $this->listcount[$this->listnum] = intval($tag['attribute']['start']) - 1; 12256 } else { 12257 $this->listcount[$this->listnum] = 0; 12258 } 12259 if ($this->rtl) { 12260 $this->rMargin += $this->listindent; 12261 } else { 12262 $this->lMargin += $this->listindent; 12263 } 12264 $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], false); 12265 $this->htmlvspace = 0; 12266 break; 12267 } 12268 case 'li': { 12269 $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false); 12270 if ($this->listordered[$this->listnum]) { 12271 // ordered item 12272 if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) { 12273 $this->lispacer = $parent['attribute']['type']; 12274 } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) { 12275 $this->lispacer = $parent['listtype']; 12276 } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) { 12277 $this->lispacer = $this->lisymbol; 12278 } else { 12279 $this->lispacer = '#'; 12280 } 12281 ++$this->listcount[$this->listnum]; 12282 if (isset($tag['attribute']['value'])) { 12283 $this->listcount[$this->listnum] = intval($tag['attribute']['value']); 12284 } 12285 } else { 12286 // unordered item 12287 if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) { 12288 $this->lispacer = $parent['attribute']['type']; 12289 } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) { 12290 $this->lispacer = $parent['listtype']; 12291 } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) { 12292 $this->lispacer = $this->lisymbol; 12293 } else { 12294 $this->lispacer = '!'; 12295 } 12296 } 12297 break; 12298 } 12299 case 'blockquote': { 12300 if ($this->rtl) { 12301 $this->rMargin += $this->listindent; 12302 } else { 12303 $this->lMargin += $this->listindent; 12304 } 12305 $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], false); 12306 break; 12307 } 12308 case 'br': { 12309 $this->Ln('', $cell); 12310 break; 12311 } 12312 case 'div': { 12313 $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false); 12314 break; 12315 } 12316 case 'p': { 12317 $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], false); 12318 break; 12319 } 12320 case 'pre': { 12321 $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false); 12322 $this->premode = true; 12323 break; 12324 } 12325 case 'sup': { 12326 $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k)); 12327 break; 12328 } 12329 case 'sub': { 12330 $this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k)); 12331 break; 12332 } 12333 case 'h1': 12334 case 'h2': 12335 case 'h3': 12336 case 'h4': 12337 case 'h5': 12338 case 'h6': { 12339 $this->addHTMLVertSpace(1, $cell, ($tag['fontsize'] * 1.5) / $this->k, $firstorlast, $tag['value'], false); 12340 break; 12341 } 12342 case 'tcpdf': { 12343 // NOT HTML: used to call TCPDF methods 12344 if (isset($tag['attribute']['method'])) { 12345 $tcpdf_method = $tag['attribute']['method']; 12346 if (method_exists($this, $tcpdf_method)) { 12347 if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) { 12348 eval('$params = array('.$tag['attribute']['params'].');'); 12349 call_user_func_array(array($this, $tcpdf_method), $params); 12350 } else { 12351 $this->$tcpdf_method(); 12352 } 12353 $this->newline = true; 12354 } 12355 } 12356 } 12357 default: { 12358 break; 12359 } 12360 } 12361 } 12362 12363 /** 12364 * Process closing tags. 12365 * @param array $dom html dom array 12366 * @param int $key current element id 12367 * @param boolean $cell if true add the default cMargin space to each new line (default false). 12368 * @access protected 12369 */ 12370 protected function closeHTMLTagHandler(&$dom, $key, $cell=false) { 12371 $tag = $dom[$key]; 12372 $parent = $dom[($dom[$key]['parent'])]; 12373 $firstorlast = ((!isset($dom[($key + 1)])) OR ((!isset($dom[($key + 2)])) AND ($dom[($key + 1)]['value'] == 'marker'))); 12374 //Closing tag 12375 switch($tag['value']) { 12376 case 'tr': { 12377 $table_el = $dom[($dom[$key]['parent'])]['parent']; 12378 if(!isset($parent['endy'])) { 12379 $dom[($dom[$key]['parent'])]['endy'] = $this->y; 12380 $parent['endy'] = $this->y; 12381 } 12382 if(!isset($parent['endpage'])) { 12383 $dom[($dom[$key]['parent'])]['endpage'] = $this->page; 12384 $parent['endpage'] = $this->page; 12385 } 12386 // update row-spanned cells 12387 if (isset($dom[$table_el]['rowspans'])) { 12388 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) { 12389 $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1; 12390 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) { 12391 if ($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) { 12392 $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']); 12393 } elseif ($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) { 12394 $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy']; 12395 $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage']; 12396 } 12397 } 12398 } 12399 // report new endy and endpage to the rowspanned cells 12400 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) { 12401 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) { 12402 $dom[$table_el]['rowspans'][$k]['endpage'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']); 12403 $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage']; 12404 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']); 12405 $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy']; 12406 } 12407 } 12408 // update remaining rowspanned cells 12409 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) { 12410 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) { 12411 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage']; 12412 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy']; 12413 } 12414 } 12415 } 12416 $this->setPage($dom[($dom[$key]['parent'])]['endpage']); 12417 $this->y = $dom[($dom[$key]['parent'])]['endy']; 12418 if (isset($dom[$table_el]['attribute']['cellspacing'])) { 12419 $cellspacing = $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px'); 12420 $this->y += $cellspacing; 12421 } 12422 $this->Ln(0, $cell); 12423 $this->x = $parent['startx']; 12424 // account for booklet mode 12425 if ($this->page > $parent['startpage']) { 12426 if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$parent['startpage']]['orm'])) { 12427 $this->x += ($this->pagedim[$this->page]['orm'] - $this->pagedim[$parent['startpage']]['orm']); 12428 } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$parent['startpage']]['olm'])) { 12429 $this->x += ($this->pagedim[$this->page]['olm'] - $this->pagedim[$parent['startpage']]['olm']); 12430 } 12431 } 12432 break; 12433 } 12434 case 'table': { 12435 // draw borders 12436 $table_el = $parent; 12437 if ((isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0)) 12438 OR (isset($table_el['style']['border']) AND ($table_el['style']['border'] > 0))) { 12439 $border = 1; 12440 } else { 12441 $border = 0; 12442 } 12443 // fix bottom line alignment of last line before page break 12444 foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) { 12445 // update row-spanned cells 12446 if (isset($dom[($dom[$key]['parent'])]['rowspans'])) { 12447 foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) { 12448 if ($trwsp['trid'] == $trkey) { 12449 $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1; 12450 } 12451 if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] >= 0)) { 12452 $dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey; 12453 } 12454 } 12455 } 12456 if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) { 12457 $pgendy = $this->pagedim[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim[$dom[$prevtrkey]['endpage']]['bm']; 12458 $dom[$prevtrkey]['endy'] = $pgendy; 12459 // update row-spanned cells 12460 if (isset($dom[($dom[$key]['parent'])]['rowspans'])) { 12461 foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) { 12462 if (($trwsp['trid'] == $trkey) AND ($trwsp['mrowspan'] == 1) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) { 12463 $dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy; 12464 $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1; 12465 } 12466 } 12467 } 12468 } 12469 $prevtrkey = $trkey; 12470 $table_el = $dom[($dom[$key]['parent'])]; 12471 } 12472 // for each row 12473 foreach ($table_el['trids'] as $j => $trkey) { 12474 $parent = $dom[$trkey]; 12475 // for each cell on the row 12476 foreach ($parent['cellpos'] as $k => $cellpos) { 12477 if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) { 12478 $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx']; 12479 $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx']; 12480 $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy']; 12481 $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage']; 12482 $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage']; 12483 } else { 12484 $endy = $parent['endy']; 12485 $startpage = $parent['startpage']; 12486 $endpage = $parent['endpage']; 12487 } 12488 if ($endpage > $startpage) { 12489 // design borders around HTML cells. 12490 for ($page=$startpage; $page <= $endpage; ++$page) { 12491 $this->setPage($page); 12492 if ($page == $startpage) { 12493 $this->y = $parent['starty']; // put cursor at the beginning of row on the first page 12494 $ch = $this->getPageHeight() - $parent['starty'] - $this->getBreakMargin(); 12495 $cborder = $this->getBorderMode($border, $position='start'); 12496 } elseif ($page == $endpage) { 12497 $this->y = $this->tMargin; // put cursor at the beginning of last page 12498 $ch = $endy - $this->tMargin; 12499 $cborder = $this->getBorderMode($border, $position='end'); 12500 } else { 12501 $this->y = $this->tMargin; // put cursor at the beginning of the current page 12502 $ch = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin(); 12503 $cborder = $this->getBorderMode($border, $position='middle'); 12504 } 12505 if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) { 12506 $this->SetFillColorArray($cellpos['bgcolor']); 12507 $fill = true; 12508 } else { 12509 $fill = false; 12510 } 12511 $cw = abs($cellpos['endx'] - $cellpos['startx']); 12512 $this->x = $cellpos['startx']; 12513 // account for margin changes 12514 if ($page > $startpage) { 12515 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) { 12516 $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']); 12517 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['lm'] != $this->pagedim[$startpage]['olm'])) { 12518 $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']); 12519 } 12520 } 12521 // design a cell around the text 12522 $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, '', $cborder, 1, '', $fill, '', 0, true); 12523 if ($cborder OR $fill) { 12524 $pagebuff = $this->getPageBuffer($this->page); 12525 $pstart = substr($pagebuff, 0, $this->intmrk[$this->page]); 12526 $pend = substr($pagebuff, $this->intmrk[$this->page]); 12527 $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend); 12528 $this->intmrk[$this->page] += strlen($ccode."\n"); 12529 } 12530 } 12531 } else { 12532 $this->setPage($startpage); 12533 if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) { 12534 $this->SetFillColorArray($cellpos['bgcolor']); 12535 $fill = true; 12536 } else { 12537 $fill = false; 12538 } 12539 $this->x = $cellpos['startx']; 12540 $this->y = $parent['starty']; 12541 $cw = abs($cellpos['endx'] - $cellpos['startx']); 12542 $ch = $endy - $parent['starty']; 12543 // design a cell around the text 12544 $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, '', $border, 1, '', $fill, '', 0, true); 12545 if ($border OR $fill) { 12546 if (end($this->transfmrk[$this->page]) !== false) { 12547 $pagemarkkey = key($this->transfmrk[$this->page]); 12548 $pagemark = &$this->transfmrk[$this->page][$pagemarkkey]; 12549 } elseif ($this->InFooter) { 12550 $pagemark = &$this->footerpos[$this->page]; 12551 } else { 12552 $pagemark = &$this->intmrk[$this->page]; 12553 } 12554 $pagebuff = $this->getPageBuffer($this->page); 12555 $pstart = substr($pagebuff, 0, $pagemark); 12556 $pend = substr($pagebuff, $pagemark); 12557 $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend); 12558 $pagemark += strlen($ccode."\n"); 12559 } 12560 } 12561 } 12562 if (isset($table_el['attribute']['cellspacing'])) { 12563 $cellspacing = $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px'); 12564 $this->y += $cellspacing; 12565 } 12566 $this->Ln(0, $cell); 12567 $this->x = $parent['startx']; 12568 if ($endpage > $startpage) { 12569 if (($this->rtl) AND ($this->pagedim[$endpage]['orm'] != $this->pagedim[$startpage]['orm'])) { 12570 $this->x += ($this->pagedim[$endpage]['orm'] - $this->pagedim[$startpage]['orm']); 12571 } elseif ((!$this->rtl) AND ($this->pagedim[$endpage]['olm'] != $this->pagedim[$startpage]['olm'])) { 12572 $this->x += ($this->pagedim[$endpage]['olm'] - $this->pagedim[$startpage]['olm']); 12573 } 12574 } 12575 } 12576 if (isset($parent['cellpadding'])) { 12577 $this->cMargin = $this->oldcMargin; 12578 } 12579 $this->lasth = $this->FontSize * $this->cell_height_ratio; 12580 if (!$this->empty_string($this->theadMargin)) { 12581 // restore top margin 12582 $this->tMargin = $this->theadMargin; 12583 $this->pagedim[$this->page]['tm'] = $this->theadMargin; 12584 } 12585 // reset table header 12586 $this->thead = ''; 12587 $this->theadMargin = ''; 12588 break; 12589 } 12590 case 'a': { 12591 $this->HREF = ''; 12592 break; 12593 } 12594 case 'sup': { 12595 $this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k)); 12596 break; 12597 } 12598 case 'sub': { 12599 $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize'])/$this->k)); 12600 break; 12601 } 12602 case 'div': { 12603 $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], true); 12604 break; 12605 } 12606 case 'blockquote': { 12607 if ($this->rtl) { 12608 $this->rMargin -= $this->listindent; 12609 } else { 12610 $this->lMargin -= $this->listindent; 12611 } 12612 $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true); 12613 break; 12614 } 12615 case 'p': { 12616 $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true); 12617 break; 12618 } 12619 case 'pre': { 12620 $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], true); 12621 $this->premode = false; 12622 break; 12623 } 12624 case 'dl': { 12625 --$this->listnum; 12626 if ($this->listnum <= 0) { 12627 $this->listnum = 0; 12628 $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true); 12629 } 12630 break; 12631 } 12632 case 'dt': { 12633 $this->lispacer = ''; 12634 $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], true); 12635 break; 12636 } 12637 case 'dd': { 12638 $this->lispacer = ''; 12639 if ($this->rtl) { 12640 $this->rMargin -= $this->listindent; 12641 } else { 12642 $this->lMargin -= $this->listindent; 12643 } 12644 $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], true); 12645 break; 12646 } 12647 case 'ul': 12648 case 'ol': { 12649 --$this->listnum; 12650 $this->lispacer = ''; 12651 if ($this->rtl) { 12652 $this->rMargin -= $this->listindent; 12653 } else { 12654 $this->lMargin -= $this->listindent; 12655 } 12656 if ($this->listnum <= 0) { 12657 $this->listnum = 0; 12658 $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true); 12659 } 12660 $this->lasth = $this->FontSize * $this->cell_height_ratio; 12661 break; 12662 } 12663 case 'li': { 12664 $this->lispacer = ''; 12665 $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], true); 12666 break; 12667 } 12668 case 'h1': 12669 case 'h2': 12670 case 'h3': 12671 case 'h4': 12672 case 'h5': 12673 case 'h6': { 12674 $this->addHTMLVertSpace(1, $cell, ($parent['fontsize'] * 1.5) / $this->k, $firstorlast, $tag['value'], true); 12675 break; 12676 } 12677 default : { 12678 break; 12679 } 12680 } 12681 $this->tmprtl = false; 12682 } 12683 12684 /** 12685 * Add vertical spaces if needed. 12686 * @param int $n number of spaces to add 12687 * @param boolean $cell if true add the default cMargin space to each new line (default false). 12688 * @param string $h The height of the break. By default, the value equals the height of the last printed cell. 12689 * @param boolean $firstorlast if true do not print additional empty lines. 12690 * @param string $tag HTML tag to which this space will be applied 12691 * @param boolean $closing true if this space will be applied to a closing tag, false otherwise 12692 * @access protected 12693 */ 12694 protected function addHTMLVertSpace($n, $cell=false, $h='', $firstorlast=false, $tag='', $closing=false) { 12695 if ($firstorlast) { 12696 $this->Ln(0, $cell); 12697 $this->htmlvspace = 0; 12698 return; 12699 } 12700 if (isset($this->tagvspaces[$tag][intval($closing)]['n'])) { 12701 $n = $this->tagvspaces[$tag][intval($closing)]['n']; 12702 } 12703 if (isset($this->tagvspaces[$tag][intval($closing)]['h'])) { 12704 $h = $this->tagvspaces[$tag][intval($closing)]['h']; 12705 } 12706 if (is_string($h)) { 12707 $vsize = $n * $this->lasth; 12708 } else { 12709 $vsize = $n * $h; 12710 } 12711 if ($vsize > $this->htmlvspace) { 12712 $this->Ln(($vsize - $this->htmlvspace), $cell); 12713 $this->htmlvspace = $vsize; 12714 } 12715 } 12716 12717 /** 12718 * Set the default bullet to be used as LI bullet symbol 12719 * @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') 12720 * @access public 12721 * @since 4.0.028 (2008-09-26) 12722 */ 12723 public function setLIsymbol($symbol='!') { 12724 $symbol = strtolower($symbol); 12725 switch ($symbol) { 12726 case '!' : 12727 case '#' : 12728 case 'disc' : 12729 case 'disc' : 12730 case 'circle' : 12731 case 'square' : 12732 case '1': 12733 case 'decimal': 12734 case 'decimal-leading-zero': 12735 case 'i': 12736 case 'lower-roman': 12737 case 'I': 12738 case 'upper-roman': 12739 case 'a': 12740 case 'lower-alpha': 12741 case 'lower-latin': 12742 case 'A': 12743 case 'upper-alpha': 12744 case 'upper-latin': 12745 case 'lower-greek': { 12746 $this->lisymbol = $symbol; 12747 break; 12748 } 12749 default : { 12750 $this->lisymbol = ''; 12751 } 12752 } 12753 } 12754 12755 /** 12756 * Set the booklet mode for double-sided pages. 12757 * @param boolean $booklet true set the booklet mode on, fals eotherwise. 12758 * @param float $inner Inner page margin. 12759 * @param float $outer Outer page margin. 12760 * @access public 12761 * @since 4.2.000 (2008-10-29) 12762 */ 12763 public function SetBooklet($booklet=true, $inner=-1, $outer=-1) { 12764 $this->booklet = $booklet; 12765 if ($inner >= 0) { 12766 $this->lMargin = $inner; 12767 } 12768 if ($outer >= 0) { 12769 $this->rMargin = $outer; 12770 } 12771 } 12772 12773 /** 12774 * Swap the left and right margins. 12775 * @param boolean $reverse if true swap left and right margins. 12776 * @access protected 12777 * @since 4.2.000 (2008-10-29) 12778 */ 12779 protected function swapMargins($reverse=true) { 12780 if ($reverse) { 12781 // swap left and right margins 12782 $mtemp = $this->original_lMargin; 12783 $this->original_lMargin = $this->original_rMargin; 12784 $this->original_rMargin = $mtemp; 12785 $deltam = $this->original_lMargin - $this->original_rMargin; 12786 $this->lMargin += $deltam; 12787 $this->rMargin -= $deltam; 12788 } 12789 } 12790 12791 /** 12792 * Set the vertical spaces for HTML tags. 12793 * The array must have the following structure (example): 12794 * $tagvs = array('h1' => array(0 => array('h' => '', 'n' => 2), 1 => array('h' => 1.3, 'n' => 1))); 12795 * The first array level contains the tag names, 12796 * the second level contains 0 for opening tags or 1 for closing tags, 12797 * the third level contains the vertical space unit (h) and the number spaces to add (n). 12798 * If the h parameter is not specified, default values are used. 12799 * @param array $tagvs array of tags and relative vertical spaces. 12800 * @access public 12801 * @since 4.2.001 (2008-10-30) 12802 */ 12803 public function setHtmlVSpace($tagvs) { 12804 $this->tagvspaces = $tagvs; 12805 } 12806 12807 /** 12808 * Set custom width for list indentation. 12809 * @param float $width width of the indentation. Use negative value to disable it. 12810 * @access public 12811 * @since 4.2.007 (2008-11-12) 12812 */ 12813 public function setListIndentWidth($width) { 12814 return $this->customlistindent = floatval($width); 12815 } 12816 12817 /** 12818 * Set the top/bottom cell sides to be open or closed when the cell cross the page. 12819 * @param boolean $isopen if true keeps the top/bottom border open for the cell sides that cross the page. 12820 * @access public 12821 * @since 4.2.010 (2008-11-14) 12822 */ 12823 public function setOpenCell($isopen) { 12824 $this->opencell = $isopen; 12825 } 12826 12827 /** 12828 * Set the color and font style for HTML links. 12829 * @param array $color RGB array of colors 12830 * @param string $fontstyle additional font styles to add 12831 * @access public 12832 * @since 4.4.003 (2008-12-09) 12833 */ 12834 public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') { 12835 $this->htmlLinkColorArray = $color; 12836 $this->htmlLinkFontStyle = $fontstyle; 12837 } 12838 12839 /** 12840 * convert html string containing value and unit of measure to user's units or points. 12841 * @param string $htmlval string containing values and unit 12842 * @param string $refsize reference value in points 12843 * @param string $defaultunit default unit (can be one of the following: %, em, ex, px, in, mm, pc, pt). 12844 * @param boolean $point if true returns points, otherwise returns value in user's units 12845 * @return float value in user's unit or point if $points=true 12846 * @access public 12847 * @since 4.4.004 (2008-12-10) 12848 */ 12849 public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) { 12850 $supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt'); 12851 $retval = 0; 12852 $value = 0; 12853 $unit = 'px'; 12854 $k = $this->k; 12855 if ($points) { 12856 $k = 1; 12857 } 12858 if (in_array($defaultunit, $supportedunits)) { 12859 $unit = $defaultunit; 12860 } 12861 if (is_numeric($htmlval)) { 12862 $value = floatval($htmlval); 12863 } elseif (preg_match('/([0-9\.]+)/', $htmlval, $mnum)) { 12864 $value = floatval($mnum[1]); 12865 if (preg_match('/([a-z%]+)/', $htmlval, $munit)) { 12866 if (in_array($munit[1], $supportedunits)) { 12867 $unit = $munit[1]; 12868 } 12869 } 12870 } 12871 switch ($unit) { 12872 // percentage 12873 case '%': { 12874 $retval = (($value * $refsize) / 100); 12875 break; 12876 } 12877 // relative-size 12878 case 'em': { 12879 $retval = ($value * $refsize); 12880 break; 12881 } 12882 case 'ex': { 12883 $retval = $value * ($refsize / 2); 12884 break; 12885 } 12886 // absolute-size 12887 case 'in': { 12888 $retval = ($value * $this->dpi) / $k; 12889 break; 12890 } 12891 case 'cm': { 12892 $retval = ($value / 2.54 * $this->dpi) / $k; 12893 break; 12894 } 12895 case 'mm': { 12896 $retval = ($value / 25.4 * $this->dpi) / $k; 12897 break; 12898 } 12899 case 'pc': { 12900 // one pica is 12 points 12901 $retval = ($value * 12) / $k; 12902 break; 12903 } 12904 case 'pt': { 12905 $retval = $value / $k; 12906 break; 12907 } 12908 case 'px': { 12909 $retval = $this->pixelsToUnits($value); 12910 break; 12911 } 12912 } 12913 return $retval; 12914 } 12915 12916 /** 12917 * Returns the Roman representation of an integer number 12918 * @param int number to convert 12919 * @return string roman representation of the specified number 12920 * @access public 12921 * @since 4.4.004 (2008-12-10) 12922 */ 12923 public function intToRoman($number) { 12924 $roman = ''; 12925 while ($number >= 1000) { 12926 $roman .= 'M'; 12927 $number -= 1000; 12928 } 12929 while ($number >= 900) { 12930 $roman .= 'CM'; 12931 $number -= 900; 12932 } 12933 while ($number >= 500) { 12934 $roman .= 'D'; 12935 $number -= 500; 12936 } 12937 while ($number >= 400) { 12938 $roman .= 'CD'; 12939 $number -= 400; 12940 } 12941 while ($number >= 100) { 12942 $roman .= 'C'; 12943 $number -= 100; 12944 } 12945 while ($number >= 90) { 12946 $roman .= 'XC'; 12947 $number -= 90; 12948 } 12949 while ($number >= 50) { 12950 $roman .= 'L'; 12951 $number -= 50; 12952 } 12953 while ($number >= 40) { 12954 $roman .= 'XL'; 12955 $number -= 40; 12956 } 12957 while ($number >= 10) { 12958 $roman .= 'X'; 12959 $number -= 10; 12960 } 12961 while ($number >= 9) { 12962 $roman .= 'IX'; 12963 $number -= 9; 12964 } 12965 while ($number >= 5) { 12966 $roman .= 'V'; 12967 $number -= 5; 12968 } 12969 while ($number >= 4) { 12970 $roman .= 'IV'; 12971 $number -= 4; 12972 } 12973 while ($number >= 1) { 12974 $roman .= 'I'; 12975 --$number; 12976 } 12977 return $roman; 12978 } 12979 12980 /** 12981 * Output an HTML list bullet or ordered item symbol 12982 * @param int $listdepth list nesting level 12983 * @param string $listtype type of list 12984 * @param float $size current font size 12985 * @access protected 12986 * @since 4.4.004 (2008-12-10) 12987 */ 12988 protected function putHtmlListBullet($listdepth, $listtype='', $size=10) { 12989 $size /= $this->k; 12990 $fill = ''; 12991 $color = $this->fgcolor; 12992 $width = 0; 12993 $textitem = ''; 12994 $tmpx = $this->x; 12995 $lspace = $this->GetStringWidth(' '); 12996 if ($listtype == '!') { 12997 // set default list type for unordered list 12998 $deftypes = array('disc', 'circle', 'square'); 12999 $listtype = $deftypes[($listdepth - 1) % 3]; 13000 } elseif ($listtype == '#') { 13001 // set default list type for ordered list 13002 $listtype = 'decimal'; 13003 } 13004 switch ($listtype) { 13005 // unordered types 13006 case 'none': { 13007 break; 13008 } 13009 case 'disc': { 13010 $fill = 'F'; 13011 } 13012 case 'circle': { 13013 $fill .= 'D'; 13014 $r = $size / 6; 13015 $lspace += (2 * $r); 13016 if ($this->rtl) { 13017 $this->x += $lspace; 13018 } else { 13019 $this->x -= $lspace; 13020 } 13021 $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), $r, 0, 360, $fill, array('color'=>$color), $color, 8); 13022 break; 13023 } 13024 case 'square': { 13025 $l = $size / 3; 13026 $lspace += $l; 13027 if ($this->rtl) { 13028 $this->x += $lspace; 13029 } else { 13030 $this->x -= $lspace; 13031 } 13032 $this->Rect($this->x, ($this->y + (($this->lasth - $l)/ 2)), $l, $l, 'F', array(), $color); 13033 break; 13034 } 13035 // ordered types 13036 13037 // $this->listcount[$this->listnum]; 13038 // $textitem 13039 case '1': 13040 case 'decimal': { 13041 $textitem = $this->listcount[$this->listnum]; 13042 break; 13043 } 13044 case 'decimal-leading-zero': { 13045 $textitem = sprintf("%02d", $this->listcount[$this->listnum]); 13046 break; 13047 } 13048 case 'i': 13049 case 'lower-roman': { 13050 $textitem = strtolower($this->intToRoman($this->listcount[$this->listnum])); 13051 break; 13052 } 13053 case 'I': 13054 case 'upper-roman': { 13055 $textitem = $this->intToRoman($this->listcount[$this->listnum]); 13056 break; 13057 } 13058 case 'a': 13059 case 'lower-alpha': 13060 case 'lower-latin': { 13061 $textitem = chr(97 + $this->listcount[$this->listnum] - 1); 13062 break; 13063 } 13064 case 'A': 13065 case 'upper-alpha': 13066 case 'upper-latin': { 13067 $textitem = chr(65 + $this->listcount[$this->listnum] - 1); 13068 break; 13069 } 13070 case 'lower-greek': { 13071 $textitem = $this->unichr(945 + $this->listcount[$this->listnum] - 1); 13072 break; 13073 } 13074 /* 13075 // Types to be implemented (special handling) 13076 case 'hebrew': { 13077 break; 13078 } 13079 case 'armenian': { 13080 break; 13081 } 13082 case 'georgian': { 13083 break; 13084 } 13085 case 'cjk-ideographic': { 13086 break; 13087 } 13088 case 'hiragana': { 13089 break; 13090 } 13091 case 'katakana': { 13092 break; 13093 } 13094 case 'hiragana-iroha': { 13095 break; 13096 } 13097 case 'katakana-iroha': { 13098 break; 13099 } 13100 */ 13101 default: { 13102 $textitem = $this->listcount[$this->listnum]; 13103 } 13104 } 13105 if (!$this->empty_string($textitem)) { 13106 // print ordered item 13107 if ($this->rtl) { 13108 $textitem = '.'.$textitem; 13109 } else { 13110 $textitem = $textitem.'.'; 13111 } 13112 $lspace += $this->GetStringWidth($textitem); 13113 if ($this->rtl) { 13114 $this->x += $lspace; 13115 } else { 13116 $this->x -= $lspace; 13117 } 13118 $this->Write($this->lasth, $textitem, '', false, '', false, 0, false); 13119 } 13120 $this->x = $tmpx; 13121 $this->lispacer = ''; 13122 } 13123 13124 /** 13125 * Returns current graphic variables as array. 13126 * @return array graphic variables 13127 * @access protected 13128 * @since 4.2.010 (2008-11-14) 13129 */ 13130 protected function getGraphicVars() { 13131 $grapvars = array( 13132 'FontFamily' => $this->FontFamily, 13133 'FontStyle' => $this->FontStyle, 13134 'FontSizePt' => $this->FontSizePt, 13135 'rMargin' => $this->rMargin, 13136 'lMargin' => $this->lMargin, 13137 'cMargin' => $this->cMargin, 13138 'LineWidth' => $this->LineWidth, 13139 'linestyleWidth' => $this->linestyleWidth, 13140 'linestyleCap' => $this->linestyleCap, 13141 'linestyleJoin' => $this->linestyleJoin, 13142 'linestyleDash' => $this->linestyleDash, 13143 'DrawColor' => $this->DrawColor, 13144 'FillColor' => $this->FillColor, 13145 'TextColor' => $this->TextColor, 13146 'ColorFlag' => $this->ColorFlag, 13147 'bgcolor' => $this->bgcolor, 13148 'fgcolor' => $this->fgcolor, 13149 'htmlvspace' => $this->htmlvspace, 13150 'lasth' => $this->lasth 13151 ); 13152 return $grapvars; 13153 } 13154 13155 /** 13156 * Set graphic variables. 13157 * @param $gvars array graphic variables 13158 * @access protected 13159 * @since 4.2.010 (2008-11-14) 13160 */ 13161 protected function setGraphicVars($gvars) { 13162 $this->FontFamily = $gvars['FontFamily']; 13163 $this->FontStyle = $gvars['FontStyle']; 13164 $this->FontSizePt = $gvars['FontSizePt']; 13165 $this->rMargin = $gvars['rMargin']; 13166 $this->lMargin = $gvars['lMargin']; 13167 $this->cMargin = $gvars['cMargin']; 13168 $this->LineWidth = $gvars['LineWidth']; 13169 $this->linestyleWidth = $gvars['linestyleWidth']; 13170 $this->linestyleCap = $gvars['linestyleCap']; 13171 $this->linestyleJoin = $gvars['linestyleJoin']; 13172 $this->linestyleDash = $gvars['linestyleDash']; 13173 $this->DrawColor = $gvars['DrawColor']; 13174 $this->FillColor = $gvars['FillColor']; 13175 $this->TextColor = $gvars['TextColor']; 13176 $this->ColorFlag = $gvars['ColorFlag']; 13177 $this->bgcolor = $gvars['bgcolor']; 13178 $this->fgcolor = $gvars['fgcolor']; 13179 $this->htmlvspace = $gvars['htmlvspace']; 13180 //$this->lasth = $gvars['lasth']; 13181 $this->_out(''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor.''); 13182 if (!$this->empty_string($this->FontFamily)) { 13183 $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt); 13184 } 13185 } 13186 13187 /** 13188 * Returns a temporary filename for caching object on filesystem. 13189 * @param string $prefix prefix to add to filename 13190 * return string filename. 13191 * @access protected 13192 * @since 4.5.000 (2008-12-31) 13193 */ 13194 protected function getObjFilename($name) { 13195 return tempnam(K_PATH_CACHE, $name.'_'); 13196 } 13197 13198 /** 13199 * Writes data to a temporary file on filesystem. 13200 * @param string $file file name 13201 * @param mixed $data data to write on file 13202 * @param boolean $append if true append data, false replace. 13203 * @access protected 13204 * @since 4.5.000 (2008-12-31) 13205 */ 13206 protected function writeDiskCache($filename, $data, $append=false) { 13207 if ($append) { 13208 $fmode = 'ab+'; 13209 } else { 13210 $fmode = 'wb+'; 13211 } 13212 $f = @fopen($filename, $fmode); 13213 if (!$f) { 13214 $this->Error('Unable to write cache file: '.$filename); 13215 } else { 13216 fwrite($f, $data); 13217 fclose($f); 13218 } 13219 // update file lenght (needed for transactions) 13220 if (!isset($this->cache_file_lenght['_'.$filename])) { 13221 $this->cache_file_lenght['_'.$filename] = strlen($data); 13222 } else { 13223 $this->cache_file_lenght['_'.$filename] += strlen($data); 13224 } 13225 } 13226 13227 /** 13228 * Read data from a temporary file on filesystem. 13229 * @param string $file file name 13230 * @return mixed retrieved data 13231 * @access protected 13232 * @since 4.5.000 (2008-12-31) 13233 */ 13234 protected function readDiskCache($filename) { 13235 return file_get_contents($filename); 13236 } 13237 13238 /** 13239 * Set buffer content (always append data). 13240 * @param string $data data 13241 * @access protected 13242 * @since 4.5.000 (2009-01-02) 13243 */ 13244 protected function setBuffer($data) { 13245 $this->bufferlen += strlen($data); 13246 if ($this->diskcache) { 13247 if (!isset($this->buffer) OR $this->empty_string($this->buffer)) { 13248 $this->buffer = $this->getObjFilename('buffer'); 13249 } 13250 $this->writeDiskCache($this->buffer, $data, true); 13251 } else { 13252 $this->buffer .= $data; 13253 } 13254 } 13255 13256 /** 13257 * Get buffer content. 13258 * @return string buffer content 13259 * @access protected 13260 * @since 4.5.000 (2009-01-02) 13261 */ 13262 protected function getBuffer() { 13263 if ($this->diskcache) { 13264 return $this->readDiskCache($this->buffer); 13265 } else { 13266 return $this->buffer; 13267 } 13268 } 13269 13270 /** 13271 * Set page buffer content. 13272 * @param int $page page number 13273 * @param string $data page data 13274 * @param boolean $append if true append data, false replace. 13275 * @access protected 13276 * @since 4.5.000 (2008-12-31) 13277 */ 13278 protected function setPageBuffer($page, $data, $append=false) { 13279 if ($this->diskcache) { 13280 if (!isset($this->pages[$page])) { 13281 $this->pages[$page] = $this->getObjFilename('page'.$page); 13282 } 13283 $this->writeDiskCache($this->pages[$page], $data, $append); 13284 } else { 13285 if ($append) { 13286 $this->pages[$page] .= $data; 13287 } else { 13288 $this->pages[$page] = $data; 13289 } 13290 } 13291 if ($append AND isset($this->pagelen[$page])) { 13292 $this->pagelen[$page] += strlen($data); 13293 } else { 13294 $this->pagelen[$page] = strlen($data); 13295 } 13296 } 13297 13298 /** 13299 * Get page buffer content. 13300 * @param int $page page number 13301 * @return string page buffer content or false in case of error 13302 * @access protected 13303 * @since 4.5.000 (2008-12-31) 13304 */ 13305 protected function getPageBuffer($page) { 13306 if ($this->diskcache) { 13307 return $this->readDiskCache($this->pages[$page]); 13308 } elseif (isset($this->pages[$page])) { 13309 return $this->pages[$page]; 13310 } 13311 return false; 13312 } 13313 13314 /** 13315 * Set image buffer content. 13316 * @param string $image image key 13317 * @param array $data image data 13318 * @access protected 13319 * @since 4.5.000 (2008-12-31) 13320 */ 13321 protected function setImageBuffer($image, $data) { 13322 if ($this->diskcache) { 13323 if (!isset($this->images[$image])) { 13324 $this->images[$image] = $this->getObjFilename('image'.$image); 13325 } 13326 $this->writeDiskCache($this->images[$image], serialize($data)); 13327 } else { 13328 $this->images[$image] = $data; 13329 } 13330 if (!in_array($image, $this->imagekeys)) { 13331 $this->imagekeys[] = $image; 13332 } 13333 ++$this->numimages; 13334 } 13335 13336 /** 13337 * Set image buffer content. 13338 * @param string $image image key 13339 * @param string $key image sub-key 13340 * @param array $data image data 13341 * @access protected 13342 * @since 4.5.000 (2008-12-31) 13343 */ 13344 protected function setImageSubBuffer($image, $key, $data) { 13345 if (!isset($this->images[$image])) { 13346 $this->setImageBuffer($image, array()); 13347 } 13348 if ($this->diskcache) { 13349 $tmpimg = $this->getImageBuffer($image); 13350 $tmpimg[$key] = $data; 13351 $this->writeDiskCache($this->images[$image], serialize($tmpimg)); 13352 } else { 13353 $this->images[$image][$key] = $data; 13354 } 13355 } 13356 13357 /** 13358 * Get page buffer content. 13359 * @param string $image image key 13360 * @return string image buffer content or false in case of error 13361 * @access protected 13362 * @since 4.5.000 (2008-12-31) 13363 */ 13364 protected function getImageBuffer($image) { 13365 if ($this->diskcache AND isset($this->images[$image])) { 13366 return unserialize($this->readDiskCache($this->images[$image])); 13367 } elseif (isset($this->images[$image])) { 13368 return $this->images[$image]; 13369 } 13370 return false; 13371 } 13372 13373 /** 13374 * Set font buffer content. 13375 * @param string $font font key 13376 * @param array $data font data 13377 * @access protected 13378 * @since 4.5.000 (2009-01-02) 13379 */ 13380 protected function setFontBuffer($font, $data) { 13381 if ($this->diskcache) { 13382 if (!isset($this->fonts[$font])) { 13383 $this->fonts[$font] = $this->getObjFilename('font'); 13384 } 13385 $this->writeDiskCache($this->fonts[$font], serialize($data)); 13386 } else { 13387 $this->fonts[$font] = $data; 13388 } 13389 if (!in_array($font, $this->fontkeys)) { 13390 $this->fontkeys[] = $font; 13391 } 13392 } 13393 13394 /** 13395 * Set font buffer content. 13396 * @param string $font font key 13397 * @param string $key font sub-key 13398 * @param array $data font data 13399 * @access protected 13400 * @since 4.5.000 (2009-01-02) 13401 */ 13402 protected function setFontSubBuffer($font, $key, $data) { 13403 if (!isset($this->fonts[$font])) { 13404 $this->setFontBuffer($font, array()); 13405 } 13406 if ($this->diskcache) { 13407 $tmpfont = $this->getFontBuffer($font); 13408 $tmpfont[$key] = $data; 13409 $this->writeDiskCache($this->fonts[$font], serialize($tmpfont)); 13410 } else { 13411 $this->fonts[$font][$key] = $data; 13412 } 13413 } 13414 13415 /** 13416 * Get font buffer content. 13417 * @param string $font font key 13418 * @return string font buffer content or false in case of error 13419 * @access protected 13420 * @since 4.5.000 (2009-01-02) 13421 */ 13422 protected function getFontBuffer($font) { 13423 if ($this->diskcache AND isset($this->fonts[$font])) { 13424 return unserialize($this->readDiskCache($this->fonts[$font])); 13425 } elseif (isset($this->fonts[$font])) { 13426 return $this->fonts[$font]; 13427 } 13428 return false; 13429 } 13430 13431 /** 13432 * Move a page to a previous position. 13433 * @param int $frompage number of the source page 13434 * @param int $topage number of the destination page (must be less than $frompage) 13435 * @return true in case of success, false in case of error. 13436 * @access public 13437 * @since 4.5.000 (2009-01-02) 13438 */ 13439 public function movePage($frompage, $topage) { 13440 if (($frompage > $this->numpages) OR ($frompage <= $topage)) { 13441 return false; 13442 } 13443 if ($frompage == $this->page) { 13444 // close the page before moving it 13445 $this->endPage(); 13446 } 13447 // move all page-related states 13448 $tmppage = $this->pages[$frompage]; 13449 $tmppagedim = $this->pagedim[$frompage]; 13450 $tmppagelen = $this->pagelen[$frompage]; 13451 $tmpintmrk = $this->intmrk[$frompage]; 13452 if (isset($this->footerpos[$frompage])) { 13453 $tmpfooterpos = $this->footerpos[$frompage]; 13454 } 13455 if (isset($this->footerlen[$frompage])) { 13456 $tmpfooterlen = $this->footerlen[$frompage]; 13457 } 13458 if (isset($this->transfmrk[$frompage])) { 13459 $tmptransfmrk = $this->transfmrk[$frompage]; 13460 } 13461 if (isset($this->PageAnnots[$frompage])) { 13462 $tmpannots = $this->PageAnnots[$frompage]; 13463 } 13464 if (isset($this->newpagegroup[$frompage])) { 13465 $tmpnewpagegroup = $this->newpagegroup[$frompage]; 13466 } 13467 for ($i = $frompage; $i > $topage; --$i) { 13468 $j = $i - 1; 13469 // shift pages down 13470 $this->pages[$i] = $this->pages[$j]; 13471 $this->pagedim[$i] = $this->pagedim[$j]; 13472 $this->pagelen[$i] = $this->pagelen[$j]; 13473 $this->intmrk[$i] = $this->intmrk[$j]; 13474 if (isset($this->footerpos[$j])) { 13475 $this->footerpos[$i] = $this->footerpos[$j]; 13476 } elseif (isset($this->footerpos[$i])) { 13477 unset($this->footerpos[$i]); 13478 } 13479 if (isset($this->footerlen[$j])) { 13480 $this->footerlen[$i] = $this->footerlen[$j]; 13481 } elseif (isset($this->footerlen[$i])) { 13482 unset($this->footerlen[$i]); 13483 } 13484 if (isset($this->transfmrk[$j])) { 13485 $this->transfmrk[$i] = $this->transfmrk[$j]; 13486 } elseif (isset($this->transfmrk[$i])) { 13487 unset($this->transfmrk[$i]); 13488 } 13489 if (isset($this->PageAnnots[$j])) { 13490 $this->PageAnnots[$i] = $this->PageAnnots[$j]; 13491 } elseif (isset($this->PageAnnots[$i])) { 13492 unset($this->PageAnnots[$i]); 13493 } 13494 if (isset($this->newpagegroup[$j])) { 13495 $this->newpagegroup[$i] = $this->newpagegroup[$j]; 13496 } elseif (isset($this->newpagegroup[$i])) { 13497 unset($this->newpagegroup[$i]); 13498 } 13499 } 13500 $this->pages[$topage] = $tmppage; 13501 $this->pagedim[$topage] = $tmppagedim; 13502 $this->pagelen[$topage] = $tmppagelen; 13503 $this->intmrk[$topage] = $tmpintmrk; 13504 if (isset($tmpfooterpos)) { 13505 $this->footerpos[$topage] = $tmpfooterpos; 13506 } elseif (isset($this->footerpos[$topage])) { 13507 unset($this->footerpos[$topage]); 13508 } 13509 if (isset($tmpfooterlen)) { 13510 $this->footerlen[$topage] = $tmpfooterlen; 13511 } elseif (isset($this->footerlen[$topage])) { 13512 unset($this->footerlen[$topage]); 13513 } 13514 if (isset($tmptransfmrk)) { 13515 $this->transfmrk[$topage] = $tmptransfmrk; 13516 } elseif (isset($this->transfmrk[$topage])) { 13517 unset($this->transfmrk[$topage]); 13518 } 13519 if (isset($tmpannots)) { 13520 $this->PageAnnots[$topage] = $tmpannots; 13521 } elseif (isset($this->PageAnnots[$topage])) { 13522 unset($this->PageAnnots[$topage]); 13523 } 13524 if (isset($tmpnewpagegroup)) { 13525 $this->newpagegroup[$topage] = $tmpnewpagegroup; 13526 } elseif (isset($this->newpagegroup[$topage])) { 13527 unset($this->newpagegroup[$topage]); 13528 } 13529 // adjust outlines 13530 $tmpoutlines = $this->outlines; 13531 foreach ($tmpoutlines as $key => $outline) { 13532 if (($outline['p'] >= $topage) AND ($outline['p'] < $frompage)) { 13533 $this->outlines[$key]['p'] = $outline['p'] + 1; 13534 } elseif ($outline['p'] == $frompage) { 13535 $this->outlines[$key]['p'] = $topage; 13536 } 13537 } 13538 // adjust links 13539 $tmplinks = $this->links; 13540 foreach ($tmplinks as $key => $link) { 13541 if (($link[0] >= $topage) AND ($link[0] < $frompage)) { 13542 $this->links[$key][0] = $link[0] + 1; 13543 } elseif ($link[0] == $frompage) { 13544 $this->links[$key][0] = $topage; 13545 } 13546 } 13547 // adjust javascript 13548 $tmpjavascript = $this->javascript; 13549 global $jfrompage, $jtopage; 13550 $jfrompage = $frompage; 13551 $jtopage = $topage; 13552 $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', 13553 create_function('$matches', 'global $jfrompage, $jtopage; 13554 $pagenum = intval($matches[3]) + 1; 13555 if (($pagenum >= $jtopage) AND ($pagenum < $jfrompage)) { 13556 $newpage = ($pagenum + 1); 13557 } elseif ($pagenum == $jfrompage) { 13558 $newpage = $jtopage; 13559 } else { 13560 $newpage = $pagenum; 13561 } 13562 --$newpage; 13563 return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript); 13564 // return to last page 13565 $this->lastPage(true); 13566 return true; 13567 } 13568 13569 /** 13570 * Remove the specified page. 13571 * @param int $page page to remove 13572 * @return true in case of success, false in case of error. 13573 * @access public 13574 * @since 4.6.004 (2009-04-23) 13575 */ 13576 public function deletePage($page) { 13577 if ($page > $this->numpages) { 13578 return false; 13579 } 13580 // delete current page 13581 unset($this->pages[$page]); 13582 unset($this->pagedim[$page]); 13583 unset($this->pagelen[$page]); 13584 unset($this->intmrk[$page]); 13585 if (isset($this->footerpos[$page])) { 13586 unset($this->footerpos[$page]); 13587 } 13588 if (isset($this->footerlen[$page])) { 13589 unset($this->footerlen[$page]); 13590 } 13591 if (isset($this->transfmrk[$page])) { 13592 unset($this->transfmrk[$page]); 13593 } 13594 if (isset($this->PageAnnots[$page])) { 13595 unset($this->PageAnnots[$page]); 13596 } 13597 if (isset($this->newpagegroup[$page])) { 13598 unset($this->newpagegroup[$page]); 13599 } 13600 if (isset($this->pageopen[$page])) { 13601 unset($this->pageopen[$page]); 13602 } 13603 // update remaining pages 13604 for ($i = $page; $i < $this->numpages; ++$i) { 13605 $j = $i + 1; 13606 // shift pages 13607 $this->pages[$i] = $this->pages[$j]; 13608 $this->pagedim[$i] = $this->pagedim[$j]; 13609 $this->pagelen[$i] = $this->pagelen[$j]; 13610 $this->intmrk[$i] = $this->intmrk[$j]; 13611 if (isset($this->footerpos[$j])) { 13612 $this->footerpos[$i] = $this->footerpos[$j]; 13613 } elseif (isset($this->footerpos[$i])) { 13614 unset($this->footerpos[$i]); 13615 } 13616 if (isset($this->footerlen[$j])) { 13617 $this->footerlen[$i] = $this->footerlen[$j]; 13618 } elseif (isset($this->footerlen[$i])) { 13619 unset($this->footerlen[$i]); 13620 } 13621 if (isset($this->transfmrk[$j])) { 13622 $this->transfmrk[$i] = $this->transfmrk[$j]; 13623 } elseif (isset($this->transfmrk[$i])) { 13624 unset($this->transfmrk[$i]); 13625 } 13626 if (isset($this->PageAnnots[$j])) { 13627 $this->PageAnnots[$i] = $this->PageAnnots[$j]; 13628 } elseif (isset($this->PageAnnots[$i])) { 13629 unset($this->PageAnnots[$i]); 13630 } 13631 if (isset($this->newpagegroup[$j])) { 13632 $this->newpagegroup[$i] = $this->newpagegroup[$j]; 13633 } elseif (isset($this->newpagegroup[$i])) { 13634 unset($this->newpagegroup[$i]); 13635 } 13636 if (isset($this->pageopen[$j])) { 13637 $this->pageopen[$i] = $this->pageopen[$j]; 13638 } elseif (isset($this->pageopen[$i])) { 13639 unset($this->pageopen[$i]); 13640 } 13641 } 13642 // remove last page 13643 unset($this->pages[$this->numpages]); 13644 unset($this->pagedim[$this->numpages]); 13645 unset($this->pagelen[$this->numpages]); 13646 unset($this->intmrk[$this->numpages]); 13647 if (isset($this->footerpos[$this->numpages])) { 13648 unset($this->footerpos[$this->numpages]); 13649 } 13650 if (isset($this->footerlen[$this->numpages])) { 13651 unset($this->footerlen[$this->numpages]); 13652 } 13653 if (isset($this->transfmrk[$this->numpages])) { 13654 unset($this->transfmrk[$this->numpages]); 13655 } 13656 if (isset($this->PageAnnots[$this->numpages])) { 13657 unset($this->PageAnnots[$this->numpages]); 13658 } 13659 if (isset($this->newpagegroup[$this->numpages])) { 13660 unset($this->newpagegroup[$this->numpages]); 13661 } 13662 if (isset($this->pageopen[$this->numpages])) { 13663 unset($this->pageopen[$this->numpages]); 13664 } 13665 --$this->numpages; 13666 $this->page = $this->numpages; 13667 // adjust outlines 13668 $tmpoutlines = $this->outlines; 13669 foreach ($tmpoutlines as $key => $outline) { 13670 if ($outline['p'] > $page) { 13671 $this->outlines[$key]['p'] = $outline['p'] - 1; 13672 } elseif ($outline['p'] == $page) { 13673 unset($this->outlines[$key]); 13674 } 13675 } 13676 // adjust links 13677 $tmplinks = $this->links; 13678 foreach ($tmplinks as $key => $link) { 13679 if ($link[0] > $page) { 13680 $this->links[$key][0] = $link[0] - 1; 13681 } elseif ($link[0] == $page) { 13682 unset($this->links[$key]); 13683 } 13684 } 13685 // adjust javascript 13686 $tmpjavascript = $this->javascript; 13687 global $jpage; 13688 $jpage = $page; 13689 $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', 13690 create_function('$matches', 'global $jpage; 13691 $pagenum = intval($matches[3]) + 1; 13692 if ($pagenum >= $jpage) { 13693 $newpage = ($pagenum - 1); 13694 } elseif ($pagenum == $jpage) { 13695 $newpage = 1; 13696 } else { 13697 $newpage = $pagenum; 13698 } 13699 --$newpage; 13700 return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript); 13701 // return to last page 13702 $this->lastPage(true); 13703 return true; 13704 } 13705 13706 /** 13707 * Output a Table of Content Index (TOC). 13708 * You can override this method to achieve different styles. 13709 * @param int $page page number where this TOC should be inserted (leave empty for current page). 13710 * @param string $numbersfont set the font for page numbers (please use monospaced font for better alignment). 13711 * @param string $filler string used to fill the space between text and page number. 13712 * @access public 13713 * @author Nicola Asuni 13714 * @since 4.5.000 (2009-01-02) 13715 */ 13716 public function addTOC($page='', $numbersfont='', $filler='.') { 13717 $fontsize = $this->FontSizePt; 13718 $fontfamily = $this->FontFamily; 13719 $fontstyle = $this->FontStyle; 13720 $w = $this->w - $this->lMargin - $this->rMargin; 13721 $spacer = $this->GetStringWidth(' ') * 4; 13722 $page_first = $this->getPage(); 13723 $lmargin = $this->lMargin; 13724 $rmargin = $this->rMargin; 13725 $x_start = $this->GetX(); 13726 if ($this->empty_string($numbersfont)) { 13727 $numbersfont = $this->default_monospaced_font; 13728 } 13729 if ($this->empty_string($filler)) { 13730 $filler = ' '; 13731 } 13732 if ($this->empty_string($page)) { 13733 $gap = ' '; 13734 } else { 13735 $gap = ''; 13736 } 13737 foreach ($this->outlines as $key => $outline) { 13738 if ($this->rtl) { 13739 $aligntext = 'R'; 13740 $alignnum = 'L'; 13741 } else { 13742 $aligntext = 'L'; 13743 $alignnum = 'R'; 13744 } 13745 if ($outline['l'] == 0) { 13746 $this->SetFont($fontfamily, $fontstyle.'B', $fontsize); 13747 } else { 13748 $this->SetFont($fontfamily, $fontstyle, $fontsize - $outline['l']); 13749 } 13750 $indent = ($spacer * $outline['l']); 13751 if ($this->rtl) { 13752 $this->rMargin += $indent; 13753 $this->x -= $indent; 13754 } else { 13755 $this->lMargin += $indent; 13756 $this->x += $indent; 13757 } 13758 $link = $this->AddLink(); 13759 $this->SetLink($link, 0, $outline['p']); 13760 // write the text 13761 $this->Write(0, $outline['t'], $link, 0, $aligntext, false, 0, false, false, 0); 13762 $this->SetFont($numbersfont, $fontstyle, $fontsize); 13763 if ($this->empty_string($page)) { 13764 $pagenum = $outline['p']; 13765 } else { 13766 // placemark to be replaced with the correct number 13767 $pagenum = '{#'.($outline['p']).'}'; 13768 if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) { 13769 $pagenum = '{'.$pagenum.'}'; 13770 } 13771 } 13772 $numwidth = $this->GetStringWidth($pagenum); 13773 if ($this->rtl) { 13774 $tw = $this->x - $this->lMargin; 13775 } else { 13776 $tw = $this->w - $this->rMargin - $this->x; 13777 } 13778 $fw = $tw - $numwidth - $this->GetStringWidth(' '); 13779 $numfills = floor($fw / $this->GetStringWidth($filler)); 13780 if ($numfills > 0) { 13781 $rowfill = str_repeat($filler, $numfills); 13782 } else { 13783 $rowfill = ''; 13784 } 13785 if ($this->rtl) { 13786 $pagenum = $pagenum.$gap.$rowfill.' '; 13787 } else { 13788 $pagenum = ' '.$rowfill.$gap.$pagenum; 13789 } 13790 // write the number 13791 //$this->SetX($x_start); 13792 $this->Cell($tw, 0, $pagenum, 0, 1, $alignnum, 0, $link, 0); 13793 $this->SetX($x_start); 13794 $this->lMargin = $lmargin; 13795 $this->rMargin = $rmargin; 13796 } 13797 $page_last = $this->getPage(); 13798 $numpages = $page_last - $page_first + 1; 13799 if (!$this->empty_string($page)) { 13800 for ($p = $page_first; $p <= $page_last; ++$p) { 13801 // get page data 13802 $temppage = $this->getPageBuffer($p); 13803 for ($n = 1; $n <= $this->numpages; ++$n) { 13804 // update page numbers 13805 $k = '{#'.$n.'}'; 13806 $ku = '{'.$k.'}'; 13807 $alias_a = $this->_escape($k); 13808 $alias_au = $this->_escape('{'.$k.'}'); 13809 if ($this->isunicode) { 13810 $alias_b = $this->_escape($this->UTF8ToLatin1($k)); 13811 $alias_bu = $this->_escape($this->UTF8ToLatin1($ku)); 13812 $alias_c = $this->_escape($this->utf8StrRev($k, false, $this->tmprtl)); 13813 $alias_cu = $this->_escape($this->utf8StrRev($ku, false, $this->tmprtl)); 13814 } 13815 if ($n >= $page) { 13816 $np = $n + $numpages; 13817 } else { 13818 $np = $n; 13819 } 13820 $ns = $this->formatTOCPageNumber($np); 13821 $nu = $ns; 13822 $sdiff = strlen($k) - strlen($ns) - 1; 13823 $sdiffu = strlen($ku) - strlen($ns) - 1; 13824 $sfill = str_repeat($filler, $sdiff); 13825 $sfillu = str_repeat($filler, $sdiffu); 13826 if ($this->rtl) { 13827 $ns = $ns.' '.$sfill; 13828 $nu = $nu.' '.$sfillu; 13829 } else { 13830 $ns = $sfill.' '.$ns; 13831 $nu = $sfillu.' '.$nu; 13832 } 13833 $nu = $this->UTF8ToUTF16BE($nu, false); 13834 $temppage = str_replace($alias_au, $nu, $temppage); 13835 if ($this->isunicode) { 13836 $temppage = str_replace($alias_bu, $nu, $temppage); 13837 $temppage = str_replace($alias_cu, $nu, $temppage); 13838 $temppage = str_replace($alias_b, $ns, $temppage); 13839 $temppage = str_replace($alias_c, $ns, $temppage); 13840 } 13841 $temppage = str_replace($alias_a, $ns, $temppage); 13842 } 13843 // save changes 13844 $this->setPageBuffer($p, $temppage); 13845 } 13846 // move pages 13847 for ($i = 0; $i < $numpages; ++$i) { 13848 $this->movePage($page_last, $page); 13849 } 13850 } 13851 $this->SetFont($fontfamily, $fontstyle, $fontsize); 13852 } 13853 13854 /** 13855 * Stores a copy of the current TCPDF object used for undo operation. 13856 * @access public 13857 * @since 4.5.029 (2009-03-19) 13858 */ 13859 public function startTransaction() { 13860 if (isset($this->objcopy)) { 13861 // remove previous copy 13862 $this->commitTransaction(); 13863 } 13864 // clone current object 13865 $this->objcopy = $this->objclone($this); 13866 } 13867 13868 /** 13869 * Delete the copy of the current TCPDF object used for undo operation. 13870 * @access public 13871 * @since 4.5.029 (2009-03-19) 13872 */ 13873 public function commitTransaction() { 13874 if (isset($this->objcopy)) { 13875 $this->objcopy->_destroy(true, true); 13876 unset($this->objcopy); 13877 } 13878 } 13879 13880 /** 13881 * This method allows to undo the latest transaction by returning the latest saved TCPDF object with startTransaction(). 13882 * @return TCPDF object. 13883 * @access public 13884 * @since 4.5.029 (2009-03-19) 13885 */ 13886 public function rollbackTransaction() { 13887 if (isset($this->objcopy)) { 13888 if (isset($this->objcopy->diskcache) AND $this->objcopy->diskcache) { 13889 // truncate files to previous values 13890 foreach ($this->objcopy->cache_file_lenght as $file => $lenght) { 13891 $file = substr($file, 1); 13892 $handle = fopen($file, 'r+'); 13893 ftruncate($handle, $lenght); 13894 } 13895 } 13896 $this->_destroy(true, true); 13897 return $this->objcopy; 13898 } 13899 return $this; 13900 } 13901 13902 /** 13903 * Creates a copy of a class object 13904 * @param object $object class object to be cloned 13905 * @return cloned object 13906 * @access public 13907 * @since 4.5.029 (2009-03-19) 13908 */ 13909 public function objclone($object) { 13910 return @clone($object); 13911 } 13912 13913 /** 13914 * Determine whether a string is empty. 13915 * @param srting $str string to be checked 13916 * @return boolean true if string is empty 13917 * @access public 13918 * @since 4.5.044 (2009-04-16) 13919 */ 13920 public function empty_string($str) { 13921 return (is_null($str) OR (is_string($str) AND (strlen($str) == 0))); 13922 } 13923 //////////////////////////////////// CRM extensions //////////////////////////////////////////////////////////////////////////////////// 13924 /** 13925 * formats a given text 13926 * @param srting $maxwidth to which the text is formated, $text to be formated 13927 * @return count 13928 * @access public 13929 */ 13930 function WordWrapText(&$text, $maxwidth) 13931 { 13932 $text = trim($text); 13933 if ($text==='') 13934 return 0; 13935 $space = $this->GetStringWidth(' '); 13936 $lines = explode("\n", $text); 13937 $text = ''; 13938 $count = 0; 13939 13940 foreach ($lines as $line) 13941 { 13942 $words = preg_split('/ +/', $line); 13943 $width = 0; 13944 13945 foreach ($words as $word) 13946 { 13947 //special condition for empty lines 13948 if ((bin2hex($word) == '0d') OR ($word == '')) $word = " "; 13949 $wordwidth = $this->GetStringWidth($word); 13950 if ($width + $wordwidth <= $maxwidth) 13951 { 13952 $width += $wordwidth + $space; 13953 $text .= $word.' '; 13954 } 13955 else 13956 { 13957 $width = $wordwidth + $space; 13958 $text = rtrim($text)."\n".$word.' '; 13959 $count++; 13960 } 13961 } 13962 $text = rtrim($text)."\n"; 13963 $count++; 13964 } 13965 $text = rtrim($text); 13966 return $count; 13967 } 13968 /** 13969 * checks whether a new page must be added 13970 * @param srting $y_location current location 13971 * @return true/false 13972 * @access public 13973 */ 13974 function CheckPageBreakPDF($y_location) 13975 { 13976 //If the next line would cause an overflow, add a new page immediately 13977 if($y_location >$this->PageBreakTrigger) 13978 { 13979 $this->AddPage($this->CurOrientation); 13980 return (true); 13981 } 13982 else return (false); 13983 } 13984 /** 13985 * checks whether a new page must be added to have enough space for the summary 13986 * @param srting $h hight of the summary 13987 * @return true/false 13988 * @access public 13989 */ 13990 function CheckPageBreakSummary($h) 13991 { 13992 //If the height h would cause an overflow, add a new page immediately 13993 if($this->GetY()+$h>($this->PageBreakTrigger)) 13994 { 13995 $this->AddPage($this->CurOrientation); 13996 return (true); 13997 } 13998 else return (false); 13999 } 14000 14001 } // END OF TCPDF CLASS 14002 } 14003 //============================================================+ 14004 // END OF FILE 14005 //============================================================+ 14006 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 20:08:37 2014 | Cross-referenced by PHPXref 0.7.1 |